2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 1997--2012 Han-Wen Nienhuys <hanwen@xs4all.nl>
5 Jan Nieuwenhuizen <janneke@gnu.org>
7 LilyPond is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
12 LilyPond is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with LilyPond. If not, see <http://www.gnu.org/licenses/>.
24 - Determine auto knees based on positions if it's set by the user.
26 - the code is littered with * and / staff_space calls for
27 #'positions. Consider moving to real-world coordinates?
29 Problematic issue is user tweaks (user tweaks are in staff-coordinates.)
33 - Stems run to the Y-center of the beam.
35 - beam_translation is the offset between Y centers of the beam.
40 #include "axis-group-interface.hh"
41 #include "align-interface.hh"
42 #include "beam-scoring-problem.hh"
43 #include "beaming-pattern.hh"
44 #include "directional-element-interface.hh"
45 #include "grob-array.hh"
46 #include "international.hh"
47 #include "interval-set.hh"
52 #include "note-head.hh"
53 #include "output-def.hh"
54 #include "pointer-group-interface.hh"
55 #include "rhythmic-head.hh"
57 #include "staff-symbol.hh"
58 #include "staff-symbol-referencer.hh"
62 #if DEBUG_BEAM_SCORING
63 #include "text-interface.hh" // debug output.
64 #include "font-interface.hh" // debug output.
69 Beam_stem_segment::Beam_stem_segment ()
71 max_connect_ = 1000; // infinity
81 beam_segment_less (Beam_segment const &a, Beam_segment const &b)
83 return a.horizontal_[LEFT] < b.horizontal_[LEFT];
86 Beam_segment::Beam_segment ()
92 Beam::add_stem (Grob *me, Grob *s)
94 if (Stem::get_beam (s))
96 programming_error ("Stem already has beam");
100 Pointer_group_interface::add_grob (me, ly_symbol2scm ("stems"), s);
101 s->set_object ("beam", me->self_scm ());
102 add_bound_item (dynamic_cast<Spanner *> (me), dynamic_cast<Item *> (s));
106 Beam::get_beam_thickness (Grob *me)
108 return robust_scm2double (me->get_property ("beam-thickness"), 0)
109 * Staff_symbol_referencer::staff_space (me);
112 /* Return the translation between 2 adjoining beams. */
114 Beam::get_beam_translation (Grob *me)
116 int beam_count = get_beam_count (me);
117 Real staff_space = Staff_symbol_referencer::staff_space (me);
118 Real line = Staff_symbol_referencer::line_thickness (me);
119 Real beam_thickness = get_beam_thickness (me);
120 Real fract = robust_scm2double (me->get_property ("length-fraction"), 1.0);
122 Real beam_translation = beam_count < 4
123 ? (2 * staff_space + line - beam_thickness) / 2.0
124 : (3 * staff_space + line - beam_thickness) / 3.0;
126 return fract * beam_translation;
129 /* Maximum beam_count. */
131 Beam::get_beam_count (Grob *me)
135 extract_grob_set (me, "stems", stems);
136 for (vsize i = 0; i < stems.size (); i++)
138 Grob *stem = stems[i];
139 m = max (m, (Stem::beam_multiplicity (stem).length () + 1));
144 MAKE_SCHEME_CALLBACK (Beam, calc_normal_stems, 1);
146 Beam::calc_normal_stems (SCM smob)
148 Grob *me = unsmob_grob (smob);
150 extract_grob_set (me, "stems", stems);
151 SCM val = Grob_array::make_array ();
152 Grob_array *ga = unsmob_grob_array (val);
153 for (vsize i = 0; i < stems.size (); i++)
154 if (Stem::is_normal_stem (stems[i]))
160 MAKE_SCHEME_CALLBACK (Beam, calc_direction, 1);
162 Beam::calc_direction (SCM smob)
164 Grob *me = unsmob_grob (smob);
166 /* Beams with less than 2 two stems don't make much sense, but could happen
173 Direction dir = CENTER;
175 int count = normal_stem_count (me);
178 extract_grob_set (me, "stems", stems);
179 if (stems.size () == 0)
181 me->warning (_ ("removing beam with no stems"));
184 return SCM_UNSPECIFIED;
188 Grob *stem = first_normal_stem (me);
191 This happens for chord tremolos.
196 if (is_direction (stem->get_property_data ("direction")))
197 dir = to_dir (stem->get_property_data ("direction"));
199 dir = to_dir (stem->get_property ("default-direction"));
206 dir = get_default_dir (me);
208 consider_auto_knees (me);
213 set_stem_directions (me, dir);
216 return scm_from_int (dir);
219 /* We want a maximal number of shared beams, but if there is choice, we
220 * take the one that is closest to the end of the stem. This is for
232 position_with_maximal_common_beams (SCM left_beaming, SCM right_beaming,
236 Slice lslice = int_list_to_slice (scm_cdr (left_beaming));
240 for (int i = lslice[-left_dir];
241 (i - lslice[left_dir]) * left_dir <= 0; i += left_dir)
244 for (SCM s = scm_car (right_beaming); scm_is_pair (s); s = scm_cdr (s))
246 int k = -right_dir * scm_to_int (scm_car (s)) + i;
247 if (scm_c_memq (scm_from_int (k), left_beaming) != SCM_BOOL_F)
251 if (count >= best_count)
261 MAKE_SCHEME_CALLBACK (Beam, calc_beaming, 1)
263 Beam::calc_beaming (SCM smob)
265 Grob *me = unsmob_grob (smob);
267 extract_grob_set (me, "stems", stems);
270 last_int.set_empty ();
272 SCM last_beaming = scm_cons (SCM_EOL, scm_list_1 (scm_from_int (0)));
273 Direction last_dir = CENTER;
274 for (vsize i = 0; i < stems.size (); i++)
276 Grob *this_stem = stems[i];
277 SCM this_beaming = this_stem->get_property ("beaming");
279 Direction this_dir = get_grob_direction (this_stem);
280 if (scm_is_pair (last_beaming) && scm_is_pair (this_beaming))
282 int start_point = position_with_maximal_common_beams
283 (last_beaming, this_beaming,
284 last_dir ? last_dir : this_dir,
291 new_slice.set_empty ();
292 SCM s = index_get_cell (this_beaming, d);
293 for (; scm_is_pair (s); s = scm_cdr (s))
296 = start_point - this_dir * scm_to_int (scm_car (s));
298 new_slice.add_point (new_beam_pos);
299 scm_set_car_x (s, scm_from_int (new_beam_pos));
302 while (flip (&d) != LEFT);
304 if (!new_slice.is_empty ())
305 last_int = new_slice;
310 FIXME: what's this for?
312 SCM s = scm_cdr (this_beaming);
313 for (; scm_is_pair (s); s = scm_cdr (s))
315 int np = -this_dir * scm_to_int (scm_car (s));
316 scm_set_car_x (s, scm_from_int (np));
317 last_int.add_point (np);
321 if (scm_ilength (scm_cdr (this_beaming)) > 0)
323 last_beaming = this_beaming;
332 operator <(Beam_stem_segment const &a,
333 Beam_stem_segment const &b)
335 return a.rank_ < b.rank_;
338 typedef map<int, vector<Beam_stem_segment> > Position_stem_segments_map;
340 MAKE_SCHEME_CALLBACK (Beam, calc_beam_segments, 1);
342 Beam::calc_beam_segments (SCM smob)
344 /* ugh, this has a side-effect that we need to ensure that
345 Stem #'beaming is correct */
346 Grob *me_grob = unsmob_grob (smob);
347 (void) me_grob->get_property ("beaming");
349 Spanner *me = dynamic_cast<Spanner *> (me_grob);
351 extract_grob_set (me, "stems", stems);
353 Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
356 commonx = me->get_bound (d)->common_refpoint (commonx, X_AXIS);
357 while (flip (&d) != LEFT);
359 int gap_count = robust_scm2int (me->get_property ("gap-count"), 0);
360 Real gap_length = robust_scm2double (me->get_property ("gap"), 0.0);
362 Position_stem_segments_map stem_segments;
363 Real lt = me->layout ()->get_dimension (ly_symbol2scm ("line-thickness"));
365 /* There are two concepts of "rank" that are used in the following code.
366 The beam_rank is the vertical position of the beam (larger numbers are
367 closer to the noteheads). Beam_stem_segment.rank_, on the other hand,
368 is the horizontal position of the segment (this is incremented by two
369 for each stem; the beam segment on the right side of the stem has
370 a higher rank (by one) than its neighbour to the left). */
372 for (vsize i = 0; i < stems.size (); i++)
374 Grob *stem = stems[i];
375 Real stem_width = robust_scm2double (stem->get_property ("thickness"), 1.0) * lt;
376 Real stem_x = stem->relative_coordinate (commonx, X_AXIS);
377 SCM beaming = stem->get_property ("beaming");
381 // Find the maximum and minimum beam ranks.
382 // Given that RANKS is never reset to empty, the interval will always be
383 // smallest for the left beamlet of the first stem, and then it might grow.
384 // Do we really want this? (It only affects the tremolo gaps) --jneem
385 for (SCM s = index_get_cell (beaming, d);
386 scm_is_pair (s); s = scm_cdr (s))
388 if (!scm_is_integer (scm_car (s)))
391 int beam_rank = scm_to_int (scm_car (s));
392 ranks.add_point (beam_rank);
395 for (SCM s = index_get_cell (beaming, d);
396 scm_is_pair (s); s = scm_cdr (s))
398 if (!scm_is_integer (scm_car (s)))
401 int beam_rank = scm_to_int (scm_car (s));
402 Beam_stem_segment seg;
404 seg.stem_x_ = stem_x;
405 seg.rank_ = 2 * i + (d + 1) / 2;
406 seg.width_ = stem_width;
409 seg.max_connect_ = robust_scm2int (stem->get_property ("max-beam-connect"), 1000);
411 Direction stem_dir = get_grob_direction (stem);
414 = (stem_dir * beam_rank < (stem_dir * ranks[-stem_dir] + gap_count));
415 stem_segments[beam_rank].push_back (seg);
418 while (flip (&d) != LEFT);
421 Drul_array<Real> break_overshoot
422 = robust_scm2drul (me->get_property ("break-overshoot"),
423 Drul_array<Real> (-0.5, 0.0));
425 vector<Beam_segment> segments;
426 for (Position_stem_segments_map::const_iterator i (stem_segments.begin ());
427 i != stem_segments.end (); i++)
429 vector<Beam_stem_segment> segs = (*i).second;
430 vector_sort (segs, less<Beam_stem_segment> ());
432 Beam_segment current;
434 // Iterate over all of the segments of the current beam rank,
435 // merging the adjacent Beam_stem_segments into one Beam_segment
437 int vertical_count = (*i).first;
438 for (vsize j = 0; j < segs.size (); j++)
440 // Keeping track of the different directions here is a little tricky.
441 // segs[j].dir_ is the direction of the beam segment relative to the stem
442 // (ie. segs[j].dir_ == LEFT if the beam segment sticks out to the left of
443 // its stem) whereas event_dir refers to the edge of the beam segment that
444 // we are currently looking at (ie. if segs[j].dir_ == event_dir then we
445 // are looking at that edge of the beam segment that is furthest from its
447 Direction event_dir = LEFT;
448 Beam_stem_segment const &seg = segs[j];
451 Beam_stem_segment const &neighbor_seg = segs[j + event_dir];
452 // TODO: make names clearer? --jneem
453 // on_line_bound: whether the current segment is on the boundary of the WHOLE beam
454 // on_beam_bound: whether the current segment is on the boundary of just that part
455 // of the beam with the current beam_rank
456 bool on_line_bound = (seg.dir_ == LEFT) ? seg.stem_index_ == 0
457 : seg.stem_index_ == stems.size () - 1;
458 bool on_beam_bound = (event_dir == LEFT) ? j == 0
459 : j == segs.size () - 1;
460 bool inside_stem = (event_dir == LEFT)
461 ? seg.stem_index_ > 0
462 : seg.stem_index_ + 1 < stems.size ();
464 bool event = on_beam_bound
465 || abs (seg.rank_ - neighbor_seg.rank_) > 1
466 || (abs (vertical_count) >= seg.max_connect_
467 || abs (vertical_count) >= neighbor_seg.max_connect_);
470 // Then this edge of the current segment is irrelevant because it will
471 // be connected with the next segment in the event_dir direction.
472 // If we skip the left edge here, the right edge of
473 // the previous segment has already been skipped since
474 // the conditions are symmetric
477 current.vertical_count_ = vertical_count;
478 current.horizontal_[event_dir] = seg.stem_x_;
479 if (seg.dir_ == event_dir)
480 // then we are examining the edge of a beam segment that is furthest
484 && me->get_bound (event_dir)->break_status_dir ())
486 current.horizontal_[event_dir]
487 = (Axis_group_interface::generic_bound_extent (me->get_bound (event_dir),
488 commonx, X_AXIS)[RIGHT]
489 + event_dir * break_overshoot[event_dir]);
493 Grob *stem = stems[seg.stem_index_];
494 Drul_array<Real> beamlet_length
495 = robust_scm2interval (stem->get_property ("beamlet-default-length"), Interval (1.1, 1.1));
496 Drul_array<Real> max_proportion
497 = robust_scm2interval (stem->get_property ("beamlet-max-length-proportion"), Interval (0.75, 0.75));
498 Real length = beamlet_length[seg.dir_];
502 Grob *neighbor_stem = stems[seg.stem_index_ + event_dir];
503 Real neighbor_stem_x = neighbor_stem->relative_coordinate (commonx, X_AXIS);
505 length = min (length,
506 fabs (neighbor_stem_x - seg.stem_x_) * max_proportion[seg.dir_]);
508 current.horizontal_[event_dir] += event_dir * length;
512 // we are examining the edge of a beam segment that is closest
513 // (ie. touching, unless there is a gap) its stem.
515 current.horizontal_[event_dir] += event_dir * seg.width_ / 2;
518 current.horizontal_[event_dir] -= event_dir * gap_length;
520 if (Stem::is_invisible (seg.stem_))
523 Need to do this in case of whole notes. We don't want the
524 heads to collide with the beams.
526 extract_grob_set (seg.stem_, "note-heads", heads);
528 for (vsize k = 0; k < heads.size (); k++)
529 current.horizontal_[event_dir]
530 = event_dir * min (event_dir * current.horizontal_[event_dir],
533 * heads[k]->extent (commonx,
534 X_AXIS)[-event_dir]);
539 if (event_dir == RIGHT)
541 segments.push_back (current);
542 current = Beam_segment ();
545 while (flip (&event_dir) != LEFT);
550 SCM segments_scm = SCM_EOL;
551 SCM *tail = &segments_scm;
553 for (vsize i = 0; i < segments.size (); i++)
555 *tail = scm_cons (scm_list_2 (scm_cons (ly_symbol2scm ("vertical-count"),
556 scm_from_int (segments[i].vertical_count_)),
557 scm_cons (ly_symbol2scm ("horizontal"),
558 ly_interval2scm (segments[i].horizontal_))),
560 tail = SCM_CDRLOC (*tail);
566 MAKE_SCHEME_CALLBACK (Beam, calc_x_positions, 1);
568 Beam::calc_x_positions (SCM smob)
570 Spanner *me = unsmob_spanner (smob);
571 SCM segments = me->get_property ("beam-segments");
572 Interval x_positions;
573 x_positions.set_empty ();
574 for (SCM s = segments; scm_is_pair (s); s = scm_cdr (s))
575 x_positions.unite (robust_scm2interval (ly_assoc_get (ly_symbol2scm ("horizontal"),
578 Interval (0.0, 0.0)));
580 // Case for beams without segments (i.e. uniting two skips with a beam)
581 // TODO: should issue a warning? warning likely issued downstream, but couldn't hurt...
582 if (x_positions.is_empty ())
584 extract_grob_set (me, "stems", stems);
585 Grob *common_x = common_refpoint_of_array (stems, me, X_AXIS);
588 x_positions[d] = me->relative_coordinate (common_x, X_AXIS);
589 while (flip (&d) != LEFT);
591 return ly_interval2scm (x_positions);
595 Beam::get_beam_segments (Grob *me)
597 SCM segments_scm = me->get_property ("beam-segments");
598 vector<Beam_segment> segments;
599 for (SCM s = segments_scm; scm_is_pair (s); s = scm_cdr (s))
601 segments.push_back (Beam_segment ());
602 segments.back ().vertical_count_ = robust_scm2int (ly_assoc_get (ly_symbol2scm ("vertical-count"), scm_car (s), SCM_EOL), 0);
603 segments.back ().horizontal_ = robust_scm2interval (ly_assoc_get (ly_symbol2scm ("horizontal"), scm_car (s), SCM_EOL), Interval (0.0, 0.0));
609 MAKE_SCHEME_CALLBACK (Beam, print, 1);
611 Beam::print (SCM grob)
613 Spanner *me = unsmob_spanner (grob);
615 TODO - mild code dup for all the commonx calls.
616 Some use just common_refpoint_of_array, some (in print and
617 calc_beam_segments) use this plus calls to get_bound.
619 Figure out if there is any particular reason for this and
620 consolidate in one Beam::get_common function.
622 extract_grob_set (me, "stems", stems);
623 Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
626 commonx = me->get_bound (d)->common_refpoint (commonx, X_AXIS);
627 while (flip (&d) != LEFT);
629 vector<Beam_segment> segments = get_beam_segments (me);
631 if (!segments.size ())
634 Real blot = me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
636 SCM posns = me->get_property ("quantized-positions");
637 Interval span = robust_scm2interval (me->get_property ("X-positions"), Interval (0, 0));
639 if (!is_number_pair (posns))
641 programming_error ("no beam positions?");
642 pos = Interval (0, 0);
645 pos = ly_scm2realdrul (posns);
647 scale_drul (&pos, Staff_symbol_referencer::staff_space (me));
649 Real dy = pos[RIGHT] - pos[LEFT];
650 Real slope = (dy && span.length ()) ? dy / span.length () : 0;
652 Real beam_thickness = get_beam_thickness (me);
653 Real beam_dy = get_beam_translation (me);
655 Direction feather_dir = to_dir (me->get_property ("grow-direction"));
657 Interval placements = robust_scm2interval (me->get_property ("normalized-endpoints"), Interval (0.0, 0.0));
660 int extreme = (segments[0].vertical_count_ == 0
661 ? segments[0].vertical_count_
662 : segments.back ().vertical_count_);
664 for (vsize i = 0; i < segments.size (); i++)
666 Real local_slope = slope;
668 Makes local slope proportional to the ratio of the length of this beam
672 local_slope += (feather_dir * segments[i].vertical_count_
674 * placements.length ()
677 Stencil b = Lookup::beam (local_slope, segments[i].horizontal_.length (), beam_thickness, blot);
679 b.translate_axis (segments[i].horizontal_[LEFT], X_AXIS);
680 Real multiplier = feather_dir ? placements[LEFT] : 1.0;
682 Interval weights (1 - multiplier, multiplier);
684 if (feather_dir != LEFT)
687 // we need two translations: the normal one and
688 // the one of the lowest segment
689 int idx[] = {i, extreme};
690 Real translations[2];
692 for (int j = 0; j < 2; j++)
693 translations[j] = slope
694 * (segments[idx[j]].horizontal_[LEFT] - span.linear_combination (CENTER))
695 + pos.linear_combination (CENTER)
696 + beam_dy * segments[idx[j]].vertical_count_;
698 Real weighted_average = translations[0] * weights[LEFT] + translations[1] * weights[RIGHT];
701 Tricky. The manipulation of the variable `weighted_average' below ensures
702 that beams with a RIGHT grow direction will start from the position of the
703 lowest segment at 0, and this error will decrease and decrease over the
704 course of the beam. Something with a LEFT grow direction, on the other
705 hand, will always start in the correct place but progressively accrue
706 error at broken places. This code shifts beams up given where they are
707 in the total span length (controlled by the variable `multiplier'). To
708 better understand what it does, try commenting it out: you'll see that
709 all of the RIGHT growing beams immediately start too low and get better
710 over line breaks, whereas all of the LEFT growing beams start just right
711 and get worse over line breaks.
713 Real factor = Interval (multiplier, 1 - multiplier).linear_combination (feather_dir);
715 if (segments[0].vertical_count_ < 0 && feather_dir)
716 weighted_average += beam_dy * (segments.size () - 1) * factor;
718 b.translate_axis (weighted_average, Y_AXIS);
720 the_beam.add_stencil (b);
724 #if (DEBUG_BEAM_SCORING)
725 SCM annotation = me->get_property ("annotation");
726 if (scm_is_string (annotation))
728 extract_grob_set (me, "stems", stems);
731 This code prints the demerits for each beam. Perhaps this
732 should be switchable for those who want to twiddle with the
736 SCM properties = Font_interface::text_font_alist_chain (me);
738 properties = scm_cons (scm_acons (ly_symbol2scm ("font-size"), scm_from_int (-5), SCM_EOL),
741 Direction stem_dir = stems.size () ? to_dir (stems[0]->get_property ("direction")) : UP;
743 Stencil score = *unsmob_stencil (Text_interface::interpret_markup
744 (me->layout ()->self_scm (), properties, annotation));
746 if (!score.is_empty ())
748 score.translate_axis (me->relative_coordinate (commonx, X_AXIS), X_AXIS);
749 the_beam.add_at_edge (Y_AXIS, stem_dir, score, 1.0);
754 the_beam.translate_axis (-me->relative_coordinate (commonx, X_AXIS), X_AXIS);
755 return the_beam.smobbed_copy ();
759 Beam::get_default_dir (Grob *me)
761 extract_grob_set (me, "stems", stems);
763 Drul_array<Real> extremes (0.0, 0.0);
764 for (iterof (s, stems); s != stems.end (); s++)
766 Interval positions = Stem::head_positions (*s);
770 if (sign (positions[d]) == d)
771 extremes[d] = d * max (d * positions[d], d * extremes[d]);
773 while (flip (&d) != DOWN);
776 Drul_array<int> total (0, 0);
777 Drul_array<int> count (0, 0);
779 bool force_dir = false;
780 for (vsize i = 0; i < stems.size (); i++)
783 Direction stem_dir = CENTER;
784 SCM stem_dir_scm = s->get_property_data ("direction");
785 if (is_direction (stem_dir_scm))
787 stem_dir = to_dir (stem_dir_scm);
791 stem_dir = to_dir (s->get_property ("default-direction"));
794 stem_dir = to_dir (s->get_property ("neutral-direction"));
799 total[stem_dir] += max (int (- stem_dir * Stem::head_positions (s) [-stem_dir]), 0);
805 if (abs (extremes[UP]) > -extremes[DOWN])
807 else if (extremes[UP] < -extremes[DOWN])
811 Direction dir = CENTER;
812 Direction d = CENTER;
813 if ((d = (Direction) sign (count[UP] - count[DOWN])))
817 && (d = (Direction) sign (total[UP] / count[UP] - total[DOWN] / count[DOWN])))
819 else if ((d = (Direction) sign (total[UP] - total[DOWN])))
822 dir = to_dir (me->get_property ("neutral-direction"));
827 /* Set all stems with non-forced direction to beam direction.
828 Urg: non-forced should become `without/with unforced' direction,
829 once stem gets cleaned-up. */
831 Beam::set_stem_directions (Grob *me, Direction d)
833 extract_grob_set (me, "stems", stems);
835 for (vsize i = 0; i < stems.size (); i++)
839 SCM forcedir = s->get_property_data ("direction");
840 if (!to_dir (forcedir))
841 set_grob_direction (s, d);
846 Only try horizontal beams for knees. No reliable detection of
847 anything else is possible here, since we don't know funky-beaming
848 settings, or X-distances (slopes!) People that want sloped
849 knee-beams, should set the directions manually.
854 this routine should take into account the stemlength scoring
855 of a possible knee/nonknee beam.
858 Beam::consider_auto_knees (Grob *me)
860 SCM scm = me->get_property ("auto-knee-gap");
861 if (!scm_is_number (scm))
868 extract_grob_set (me, "normal-stems", stems);
870 Grob *common = common_refpoint_of_array (stems, me, Y_AXIS);
871 Real staff_space = Staff_symbol_referencer::staff_space (me);
873 vector<Interval> head_extents_array;
874 for (vsize i = 0; i < stems.size (); i++)
876 Grob *stem = stems[i];
878 Interval head_extents = Stem::head_positions (stem);
879 if (!head_extents.is_empty ())
881 head_extents[LEFT] += -1;
882 head_extents[RIGHT] += 1;
883 head_extents *= staff_space * 0.5;
886 We could subtract beam Y position, but this routine only
887 sets stem directions, a constant shift does not have an
890 head_extents += stem->pure_relative_y_coordinate (common, 0, INT_MAX);
892 if (to_dir (stem->get_property_data ("direction")))
894 Direction stemdir = to_dir (stem->get_property ("direction"));
895 head_extents[-stemdir] = -stemdir * infinity_f;
898 head_extents_array.push_back (head_extents);
900 gaps.remove_interval (head_extents);
904 Real max_gap_len = 0.0;
906 for (vsize i = gaps.allowed_regions_.size () - 1; i != VPOS; i--)
908 Interval gap = gaps.allowed_regions_[i];
911 the outer gaps are not knees.
913 if (isinf (gap[LEFT]) || isinf (gap[RIGHT]))
916 if (gap.length () >= max_gap_len)
918 max_gap_len = gap.length ();
923 Real beam_translation = get_beam_translation (me);
924 Real beam_thickness = Beam::get_beam_thickness (me);
925 int beam_count = Beam::get_beam_count (me);
926 Real height_of_beams = beam_thickness / 2
927 + (beam_count - 1) * beam_translation;
928 Real threshold = scm_to_double (scm) + height_of_beams;
930 if (max_gap_len > threshold)
933 for (vsize i = 0; i < stems.size (); i++)
935 Grob *stem = stems[i];
936 Interval head_extents = head_extents_array[j++];
938 Direction d = (head_extents.center () < max_gap.center ())
941 stem->set_property ("direction", scm_from_int (d));
943 head_extents.intersect (max_gap);
944 assert (head_extents.is_empty () || head_extents.length () < 1e-6);
949 MAKE_SCHEME_CALLBACK (Beam, calc_stem_shorten, 1)
951 Beam::calc_stem_shorten (SCM smob)
953 Grob *me = unsmob_grob (smob);
956 shortening looks silly for x staff beams
959 return scm_from_int (0);
961 Real forced_fraction = 1.0 * forced_stem_count (me)
962 / normal_stem_count (me);
964 int beam_count = get_beam_count (me);
966 SCM shorten_list = me->get_property ("beamed-stem-shorten");
967 if (shorten_list == SCM_EOL)
968 return scm_from_int (0);
970 Real staff_space = Staff_symbol_referencer::staff_space (me);
973 = robust_list_ref (beam_count - 1, shorten_list);
974 Real shorten = scm_to_double (shorten_elt) * staff_space;
976 shorten *= forced_fraction;
979 return scm_from_double (shorten);
981 return scm_from_double (0.0);
984 MAKE_SCHEME_CALLBACK (Beam, quanting, 3);
986 Beam::quanting (SCM smob, SCM ys_scm, SCM align_broken_intos)
988 Grob *me = unsmob_grob (smob);
989 Drul_array<Real> ys = robust_scm2drul (ys_scm, Drul_array<Real> (infinity_f, -infinity_f));
990 bool cbs = to_boolean (align_broken_intos);
992 Beam_scoring_problem problem (me, ys, cbs);
993 ys = problem.solve ();
995 return ly_interval2scm (ys);
999 Report slice containing the numbers that are both in (car BEAMING)
1003 where_are_the_whole_beams (SCM beaming)
1007 for (SCM s = scm_car (beaming); scm_is_pair (s); s = scm_cdr (s))
1009 if (scm_c_memq (scm_car (s), scm_cdr (beaming)) != SCM_BOOL_F)
1011 l.add_point (scm_to_int (scm_car (s)));
1017 /* Return the Y position of the stem-end, given the Y-left, Y-right
1018 in POS for stem S. This Y position is relative to S. */
1020 Beam::calc_stem_y (Grob *me, Grob *stem, Grob **common,
1021 Real xl, Real xr, Direction feather_dir,
1022 Drul_array<Real> pos, bool french)
1024 Real beam_translation = get_beam_translation (me);
1025 Direction stem_dir = get_grob_direction (stem);
1028 Real relx = dx ? (stem->relative_coordinate (common[X_AXIS], X_AXIS) - xl) / dx : 0;
1029 Real xdir = 2 * relx - 1;
1031 Real stem_y = linear_combination (pos, xdir);
1033 SCM beaming = stem->get_property ("beaming");
1035 Slice beam_slice (french
1036 ? where_are_the_whole_beams (beaming)
1037 : Stem::beam_multiplicity (stem));
1038 if (beam_slice.is_empty ())
1039 beam_slice = Slice (0, 0);
1040 Interval beam_multiplicity (beam_slice[LEFT],
1044 feather dir = 1 , relx 0->1 : factor 0 -> 1
1045 feather dir = 0 , relx 0->1 : factor 1 -> 1
1046 feather dir = -1, relx 0->1 : factor 1 -> 0
1048 Real feather_factor = 1;
1049 if (feather_dir > 0)
1050 feather_factor = relx;
1051 else if (feather_dir < 0)
1052 feather_factor = 1 - relx;
1054 stem_y += feather_factor * beam_translation
1055 * beam_multiplicity[Direction (((french) ? DOWN : UP) * stem_dir)];
1056 Real id = me->relative_coordinate (common[Y_AXIS], Y_AXIS)
1057 - stem->relative_coordinate (common[Y_AXIS], Y_AXIS);
1063 Hmm. At this time, beam position and slope are determined. Maybe,
1064 stem directions and length should set to relative to the chord's
1065 position of the beam. */
1066 MAKE_SCHEME_CALLBACK (Beam, set_stem_lengths, 1);
1068 Beam::set_stem_lengths (SCM smob)
1070 Grob *me = unsmob_grob (smob);
1072 /* trigger callbacks. */
1073 (void) me->get_property ("direction");
1074 (void) me->get_property ("beaming");
1076 SCM posns = me->get_property ("positions");
1078 extract_grob_set (me, "stems", stems);
1083 for (int a = 2; a--;)
1084 common[a] = common_refpoint_of_array (stems, me, Axis (a));
1086 Drul_array<Real> pos = ly_scm2realdrul (posns);
1087 Real staff_space = Staff_symbol_referencer::staff_space (me);
1088 scale_drul (&pos, staff_space);
1092 if (robust_scm2int (me->get_property ("gap-count"), 0))
1095 thick = get_beam_thickness (me);
1098 Grob *fvs = first_normal_stem (me);
1099 Grob *lvs = last_normal_stem (me);
1101 Interval x_span = robust_scm2interval (me->get_property ("X-positions"), Interval (0, 0));
1102 Direction feather_dir = to_dir (me->get_property ("grow-direction"));
1104 for (vsize i = 0; i < stems.size (); i++)
1108 bool french = to_boolean (s->get_property ("french-beaming"));
1109 Real stem_y = calc_stem_y (me, s, common,
1110 x_span[LEFT], x_span[RIGHT], feather_dir,
1111 pos, french && s != lvs && s != fvs);
1114 Make the stems go up to the end of the beam. This doesn't matter
1115 for normal beams, but for tremolo beams it looks silly otherwise.
1118 && !Stem::is_invisible (s))
1119 stem_y += thick * 0.5 * get_grob_direction (s);
1122 Do set_stem_positions for invisible stems too, so tuplet brackets
1123 have a reference point for sloping
1125 Stem::set_stem_positions (s, 2 * stem_y / staff_space);
1132 Beam::set_beaming (Grob *me, Beaming_pattern const *beaming)
1134 extract_grob_set (me, "stems", stems);
1137 for (vsize i = 0; i < stems.size (); i++)
1140 Don't overwrite user settings.
1144 Grob *stem = stems[i];
1145 SCM beaming_prop = stem->get_property ("beaming");
1146 if (beaming_prop == SCM_EOL
1147 || index_get_cell (beaming_prop, d) == SCM_EOL)
1149 int count = beaming->beamlet_count (i, d);
1151 && i + 1 < stems.size ()
1152 && Stem::is_invisible (stem))
1153 count = min (count, beaming->beamlet_count (i, -d));
1155 if ( ((i == 0 && d == LEFT)
1156 || (i == stems.size () - 1 && d == RIGHT))
1157 && stems.size () > 1
1158 && to_boolean (me->get_property ("clip-edges")))
1161 Stem::set_beaming (stem, count, d);
1164 while (flip (&d) != LEFT);
1169 Beam::forced_stem_count (Grob *me)
1171 extract_grob_set (me, "normal-stems", stems);
1174 for (vsize i = 0; i < stems.size (); i++)
1178 /* I can imagine counting those boundaries as a half forced stem,
1179 but let's count them full for now. */
1180 Direction defdir = to_dir (s->get_property ("default-direction"));
1182 if (abs (Stem::chord_start_y (s)) > 0.1
1184 && get_grob_direction (s) != defdir)
1191 Beam::normal_stem_count (Grob *me)
1193 extract_grob_set (me, "normal-stems", stems);
1194 return stems.size ();
1198 Beam::first_normal_stem (Grob *me)
1200 extract_grob_set (me, "normal-stems", stems);
1201 return stems.size () ? stems[0] : 0;
1205 Beam::last_normal_stem (Grob *me)
1207 extract_grob_set (me, "normal-stems", stems);
1208 return stems.size () ? stems.back () : 0;
1214 handle rest under beam (do_post: beams are calculated now)
1215 what about combination of collisions and rest under beam.
1219 rest -> stem -> beam -> interpolate_y_position ()
1221 MAKE_SCHEME_CALLBACK_WITH_OPTARGS (Beam, rest_collision_callback, 2, 1, "");
1223 Beam::rest_collision_callback (SCM smob, SCM prev_offset)
1225 Grob *rest = unsmob_grob (smob);
1226 if (scm_is_number (rest->get_property ("staff-position")))
1227 return scm_from_int (0);
1229 Real offset = robust_scm2double (prev_offset, 0.0);
1231 Grob *st = unsmob_grob (rest->get_object ("stem"));
1234 return scm_from_double (0.0);
1235 Grob *beam = unsmob_grob (stem->get_object ("beam"));
1237 || !Beam::has_interface (beam)
1238 || !Beam::normal_stem_count (beam))
1239 return scm_from_double (0.0);
1241 Drul_array<Real> pos (robust_scm2drul (beam->get_property ("positions"),
1242 Drul_array<Real> (0, 0)));
1244 Real staff_space = Staff_symbol_referencer::staff_space (rest);
1246 scale_drul (&pos, staff_space);
1248 Real dy = pos[RIGHT] - pos[LEFT];
1250 extract_grob_set (beam, "stems", stems);
1251 Grob *common = common_refpoint_of_array (stems, beam, X_AXIS);
1253 Interval x_span = robust_scm2interval (beam->get_property ("X-positions"),
1254 Interval (0.0, 0.0));
1255 Real x0 = x_span[LEFT];
1256 Real dx = x_span.length ();
1257 Real slope = dy && dx ? dy / dx : 0;
1259 Direction d = get_grob_direction (stem);
1260 Real stem_y = pos[LEFT]
1261 + (stem->relative_coordinate (common, X_AXIS) - x0) * slope;
1263 Real beam_translation = get_beam_translation (beam);
1264 Real beam_thickness = Beam::get_beam_thickness (beam);
1267 TODO: this is not strictly correct for 16th knee beams.
1270 = Stem::beam_multiplicity (stem).length () + 1;
1272 Real height_of_my_beams = beam_thickness / 2
1273 + (beam_count - 1) * beam_translation;
1274 Real beam_y = stem_y - d * height_of_my_beams;
1276 Grob *common_y = rest->common_refpoint (beam, Y_AXIS);
1278 Interval rest_extent = rest->extent (rest, Y_AXIS);
1279 rest_extent.translate (offset + rest->get_parent (Y_AXIS)->relative_coordinate (common_y, Y_AXIS));
1281 Real rest_dim = rest_extent[d];
1282 Real minimum_distance
1283 = staff_space * (robust_scm2double (stem->get_property ("stemlet-length"), 0.0)
1284 + robust_scm2double (rest->get_property ("minimum-distance"), 0.0));
1286 Real shift = d * min (d * (beam_y - d * minimum_distance - rest_dim), 0.0);
1288 shift /= staff_space;
1289 Real rad = Staff_symbol_referencer::line_count (rest) * staff_space / 2;
1291 /* Always move discretely by half spaces */
1292 shift = ceil (fabs (shift * 2.0)) / 2.0 * sign (shift);
1294 /* Inside staff, move by whole spaces*/
1295 if ((rest_extent[d] + staff_space * shift) * d
1297 || (rest_extent[-d] + staff_space * shift) * -d
1299 shift = ceil (fabs (shift)) * sign (shift);
1301 return scm_from_double (offset + staff_space * shift);
1304 MAKE_SCHEME_CALLBACK_WITH_OPTARGS (Beam, pure_rest_collision_callback, 4, 1, "");
1306 Beam::pure_rest_collision_callback (SCM smob,
1307 SCM, /* prev_offset */
1313 Grob *me = unsmob_grob (smob);
1314 Grob *stem = unsmob_grob (me->get_object ("stem"));
1316 return scm_from_double (amount);
1317 Grob *beam = unsmob_grob (stem->get_object ("beam"));
1319 || !Beam::normal_stem_count (beam))
1320 return scm_from_double (amount);
1322 Real ss = Staff_symbol_referencer::staff_space (me);
1325 This gives the extrema of rest positions.
1326 In general, beams are never typeset more than one staff space away
1327 from the staff in either direction.
1329 Grob *staff = Staff_symbol_referencer::get_staff_symbol (me);
1330 Interval rest_max_pos = staff ? Staff_symbol::line_span (staff) : Interval (0.0, 0.0);
1331 rest_max_pos.widen (1);
1332 rest_max_pos *= ss / 2;
1334 extract_grob_set (beam, "stems", stems);
1335 vector<Grob *> my_stems;
1337 for (vsize i = 0; i < stems.size (); i++)
1338 if (Stem::head_count (stems[i]) || stems[i] == stem)
1339 my_stems.push_back (stems[i]);
1343 for (vsize i = 0; i < my_stems.size (); i++)
1344 if (my_stems[i] == stem)
1352 if (idx == (vsize) - 1 || my_stems.size () == 1)
1353 return scm_from_double (amount);
1355 left = right = my_stems[1];
1356 else if (idx == my_stems.size () - 1)
1357 left = right = my_stems[idx - 1];
1360 left = my_stems[idx - 1];
1361 right = my_stems[idx + 1];
1363 Direction beamdir = get_grob_direction (beam);
1365 Take the position between the two bounding head_positions,
1366 then bound it by the minimum and maximum positions outside the staff.
1367 4.0 = 2.0 to get out of staff space * 2.0 for the average
1369 amount = min (max ((Stem::head_positions (left)[beamdir] + Stem::head_positions (right)[beamdir]) / 4.0, rest_max_pos[DOWN]), rest_max_pos[UP]);
1371 return scm_from_double (amount);
1375 Beam::is_knee (Grob *me)
1377 SCM k = me->get_property ("knee");
1378 if (scm_is_bool (k))
1379 return ly_scm2bool (k);
1383 extract_grob_set (me, "stems", stems);
1384 for (vsize i = stems.size (); i--;)
1386 Direction dir = get_grob_direction (stems[i]);
1395 me->set_property ("knee", ly_bool2scm (knee));
1401 Beam::is_cross_staff (Grob *me)
1403 extract_grob_set (me, "stems", stems);
1404 Grob *staff_symbol = Staff_symbol_referencer::get_staff_symbol (me);
1405 for (vsize i = 0; i < stems.size (); i++)
1406 if (Staff_symbol_referencer::get_staff_symbol (stems[i]) != staff_symbol)
1411 MAKE_SCHEME_CALLBACK (Beam, calc_cross_staff, 1)
1413 Beam::calc_cross_staff (SCM smob)
1415 return scm_from_bool (is_cross_staff (unsmob_grob (smob)));
1419 Beam::get_direction_beam_count (Grob *me, Direction d)
1421 extract_grob_set (me, "stems", stems);
1424 for (vsize i = stems.size (); i--;)
1427 Should we take invisible stems into account?
1429 if (get_grob_direction (stems[i]) == d)
1430 bc = max (bc, (Stem::beam_multiplicity (stems[i]).length () + 1));
1436 ADD_INTERFACE (Beam,
1439 "The @code{beam-thickness} property is the weight of beams,"
1440 " measured in staffspace. The @code{direction} property is"
1441 " not user-serviceable. Use the @code{direction} property"
1442 " of @code{Stem} instead.\n"
1443 "The following properties may be set in the @code{details}"
1447 "@item stem-length-demerit-factor\n"
1448 "Demerit factor used for inappropriate stem lengths.\n"
1449 "@item secondary-beam-demerit\n"
1450 "Demerit used in quanting calculations for multiple"
1452 "@item region-size\n"
1453 "Size of region for checking quant scores.\n"
1455 "Epsilon for beam quant code to check for presence"
1457 "@item stem-length-limit-penalty\n"
1458 "Penalty for differences in stem lengths on a beam.\n"
1459 "@item damping-direction-penalty\n"
1460 "Demerit penalty applied when beam direction is different"
1461 " from damping direction.\n"
1462 "@item hint-direction-penalty\n"
1463 "Demerit penalty applied when beam direction is different"
1464 " from damping direction, but damping slope is"
1465 " <= @code{round-to-zero-slope}.\n"
1466 "@item musical-direction-factor\n"
1467 "Demerit scaling factor for difference between"
1468 " beam slope and music slope.\n"
1469 "@item ideal-slope-factor\n"
1470 "Demerit scaling factor for difference between"
1471 " beam slope and damping slope.\n"
1472 "@item round-to-zero-slope\n"
1473 "Damping slope which is considered zero for purposes of"
1474 " calculating direction penalties.\n"
1480 "beamed-stem-shorten "
1487 "collision-interfaces "
1488 "collision-voice-only "
1500 "neutral-direction "
1503 "quantized-positions "