2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 1997--2015 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);
123 if fract != 1.0, as is the case for grace notes, we want the gap
124 to decrease too. To achieve this, we divide the thickness by
126 return (beam_count < 4
127 ? (2 * staff_space * fract + line * fract - beam_thickness) / 2.0
128 : (3 * staff_space * fract + line * fract - beam_thickness) / 3.0);
131 /* Maximum beam_count. */
133 Beam::get_beam_count (Grob *me)
137 extract_grob_set (me, "stems", stems);
138 for (vsize i = 0; i < stems.size (); i++)
140 Grob *stem = stems[i];
141 m = max (m, (Stem::beam_multiplicity (stem).length () + 1));
146 MAKE_SCHEME_CALLBACK (Beam, calc_normal_stems, 1);
148 Beam::calc_normal_stems (SCM smob)
150 Grob *me = unsmob<Grob> (smob);
152 extract_grob_set (me, "stems", stems);
153 SCM val = Grob_array::make_array ();
154 Grob_array *ga = unsmob<Grob_array> (val);
155 for (vsize i = 0; i < stems.size (); i++)
156 if (Stem::is_normal_stem (stems[i]))
162 MAKE_SCHEME_CALLBACK (Beam, calc_direction, 1);
164 Beam::calc_direction (SCM smob)
166 Grob *me = unsmob<Grob> (smob);
168 /* Beams with less than 2 two stems don't make much sense, but could happen
175 Direction dir = CENTER;
177 int count = normal_stem_count (me);
180 extract_grob_set (me, "stems", stems);
181 if (stems.size () == 0)
183 me->warning (_ ("removing beam with no stems"));
186 return SCM_UNSPECIFIED;
190 Grob *stem = first_normal_stem (me);
193 This happens for chord tremolos.
198 if (is_direction (stem->get_property_data ("direction")))
199 dir = to_dir (stem->get_property_data ("direction"));
201 dir = to_dir (stem->get_property ("default-direction"));
203 extract_grob_set (stem, "note-heads", heads);
204 /* default position of Kievan heads with beams is down
205 placing this here avoids warnings downstream */
208 if (scm_is_eq (heads[0]->get_property ("style"),
209 ly_symbol2scm ("kievan")))
221 dir = get_default_dir (me);
223 consider_auto_knees (me);
228 set_stem_directions (me, dir);
231 return scm_from_int (dir);
234 /* We want a maximal number of shared beams, but if there is choice, we
235 * take the one that is closest to the end of the stem. This is for
247 position_with_maximal_common_beams (SCM left_beaming, SCM right_beaming,
251 Slice lslice = int_list_to_slice (scm_cdr (left_beaming));
255 for (int i = lslice[-left_dir];
256 (i - lslice[left_dir]) * left_dir <= 0; i += left_dir)
259 for (SCM s = scm_car (right_beaming); scm_is_pair (s); s = scm_cdr (s))
261 int k = -right_dir * scm_to_int (scm_car (s)) + i;
262 if (scm_is_true (ly_memv (scm_from_int (k), left_beaming)))
266 if (count >= best_count)
276 MAKE_SCHEME_CALLBACK (Beam, calc_beaming, 1)
278 Beam::calc_beaming (SCM smob)
280 Grob *me = unsmob<Grob> (smob);
282 extract_grob_set (me, "stems", stems);
285 last_int.set_empty ();
287 SCM last_beaming = scm_cons (SCM_EOL, scm_list_1 (scm_from_int (0)));
288 Direction last_dir = CENTER;
289 for (vsize i = 0; i < stems.size (); i++)
291 Grob *this_stem = stems[i];
292 SCM this_beaming = this_stem->get_property ("beaming");
294 Direction this_dir = get_grob_direction (this_stem);
295 if (scm_is_pair (last_beaming) && scm_is_pair (this_beaming))
297 int start_point = position_with_maximal_common_beams
298 (last_beaming, this_beaming,
299 last_dir ? last_dir : this_dir,
303 for (LEFT_and_RIGHT (d))
305 new_slice.set_empty ();
306 SCM s = index_get_cell (this_beaming, d);
307 for (; scm_is_pair (s); s = scm_cdr (s))
310 = start_point - this_dir * scm_to_int (scm_car (s));
312 new_slice.add_point (new_beam_pos);
313 scm_set_car_x (s, scm_from_int (new_beam_pos));
317 if (!new_slice.is_empty ())
318 last_int = new_slice;
323 FIXME: what's this for?
325 SCM s = scm_cdr (this_beaming);
326 for (; scm_is_pair (s); s = scm_cdr (s))
328 int np = -this_dir * scm_to_int (scm_car (s));
329 scm_set_car_x (s, scm_from_int (np));
330 last_int.add_point (np);
334 if (scm_ilength (scm_cdr (this_beaming)) > 0)
336 last_beaming = this_beaming;
345 operator <(Beam_stem_segment const &a,
346 Beam_stem_segment const &b)
348 return a.rank_ < b.rank_;
351 typedef map<int, vector<Beam_stem_segment> > Position_stem_segments_map;
353 MAKE_SCHEME_CALLBACK (Beam, calc_beam_segments, 1);
355 Beam::calc_beam_segments (SCM smob)
357 /* ugh, this has a side-effect that we need to ensure that
358 Stem #'beaming is correct */
359 Grob *me_grob = unsmob<Grob> (smob);
360 (void) me_grob->get_property ("beaming");
362 Spanner *me = dynamic_cast<Spanner *> (me_grob);
364 extract_grob_set (me, "stems", stems);
366 Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
367 for (LEFT_and_RIGHT (d))
368 commonx = me->get_bound (d)->common_refpoint (commonx, X_AXIS);
370 int gap_count = robust_scm2int (me->get_property ("gap-count"), 0);
371 Real gap_length = robust_scm2double (me->get_property ("gap"), 0.0);
373 Position_stem_segments_map stem_segments;
374 Real lt = me->layout ()->get_dimension (ly_symbol2scm ("line-thickness"));
376 /* There are two concepts of "rank" that are used in the following code.
377 The beam_rank is the vertical position of the beam (larger numbers are
378 closer to the noteheads). Beam_stem_segment.rank_, on the other hand,
379 is the horizontal position of the segment (this is incremented by two
380 for each stem; the beam segment on the right side of the stem has
381 a higher rank (by one) than its neighbour to the left). */
383 for (vsize i = 0; i < stems.size (); i++)
385 Grob *stem = stems[i];
386 Real stem_width = robust_scm2double (stem->get_property ("thickness"), 1.0) * lt;
387 Real stem_x = stem->relative_coordinate (commonx, X_AXIS);
388 SCM beaming = stem->get_property ("beaming");
390 for (LEFT_and_RIGHT (d))
392 // Find the maximum and minimum beam ranks.
393 // Given that RANKS is never reset to empty, the interval will always be
394 // smallest for the left beamlet of the first stem, and then it might grow.
395 // Do we really want this? (It only affects the tremolo gaps) --jneem
396 for (SCM s = index_get_cell (beaming, d);
397 scm_is_pair (s); s = scm_cdr (s))
399 if (!scm_is_integer (scm_car (s)))
402 int beam_rank = scm_to_int (scm_car (s));
403 ranks.add_point (beam_rank);
406 for (SCM s = index_get_cell (beaming, d);
407 scm_is_pair (s); s = scm_cdr (s))
409 if (!scm_is_integer (scm_car (s)))
412 int beam_rank = scm_to_int (scm_car (s));
413 Beam_stem_segment seg;
415 seg.stem_x_ = stem_x;
416 seg.rank_ = 2 * i + (d + 1) / 2;
417 seg.width_ = stem_width;
420 seg.max_connect_ = robust_scm2int (stem->get_property ("max-beam-connect"), 1000);
422 Direction stem_dir = get_grob_direction (stem);
425 = (stem_dir * beam_rank < (stem_dir * ranks[-stem_dir] + gap_count));
426 stem_segments[beam_rank].push_back (seg);
431 Drul_array<Real> break_overshoot
432 = robust_scm2drul (me->get_property ("break-overshoot"),
433 Drul_array<Real> (-0.5, 0.0));
435 vector<Beam_segment> segments;
436 for (Position_stem_segments_map::const_iterator i (stem_segments.begin ());
437 i != stem_segments.end (); i++)
439 vector<Beam_stem_segment> segs = (*i).second;
440 vector_sort (segs, less<Beam_stem_segment> ());
442 Beam_segment current;
444 // Iterate over all of the segments of the current beam rank,
445 // merging the adjacent Beam_stem_segments into one Beam_segment
447 int vertical_count = (*i).first;
448 for (vsize j = 0; j < segs.size (); j++)
450 // Keeping track of the different directions here is a little tricky.
451 // segs[j].dir_ is the direction of the beam segment relative to the stem
452 // (ie. segs[j].dir_ == LEFT if the beam segment sticks out to the left of
453 // its stem) whereas event_dir refers to the edge of the beam segment that
454 // we are currently looking at (ie. if segs[j].dir_ == event_dir then we
455 // are looking at that edge of the beam segment that is furthest from its
457 Beam_stem_segment const &seg = segs[j];
458 for (LEFT_and_RIGHT (event_dir))
460 Beam_stem_segment const &neighbor_seg = segs[j + event_dir];
461 // TODO: make names clearer? --jneem
462 // on_line_bound: whether the current segment is on the boundary of the WHOLE beam
463 // on_beam_bound: whether the current segment is on the boundary of just that part
464 // of the beam with the current beam_rank
465 bool on_line_bound = (seg.dir_ == LEFT) ? seg.stem_index_ == 0
466 : seg.stem_index_ == stems.size () - 1;
467 bool on_beam_bound = (event_dir == LEFT) ? j == 0
468 : j == segs.size () - 1;
469 bool inside_stem = (event_dir == LEFT)
470 ? seg.stem_index_ > 0
471 : seg.stem_index_ + 1 < stems.size ();
473 bool event = on_beam_bound
474 || abs (seg.rank_ - neighbor_seg.rank_) > 1
475 || (abs (vertical_count) >= seg.max_connect_
476 || abs (vertical_count) >= neighbor_seg.max_connect_);
479 // Then this edge of the current segment is irrelevant because it will
480 // be connected with the next segment in the event_dir direction.
481 // If we skip the left edge here, the right edge of
482 // the previous segment has already been skipped since
483 // the conditions are symmetric
486 current.vertical_count_ = vertical_count;
487 current.horizontal_[event_dir] = seg.stem_x_;
488 if (seg.dir_ == event_dir)
489 // then we are examining the edge of a beam segment that is furthest
493 && me->get_bound (event_dir)->break_status_dir ())
495 current.horizontal_[event_dir]
496 = (Axis_group_interface::generic_bound_extent (me->get_bound (event_dir),
497 commonx, X_AXIS)[RIGHT]
498 + event_dir * break_overshoot[event_dir]);
502 Grob *stem = stems[seg.stem_index_];
503 Drul_array<Real> beamlet_length
504 = robust_scm2interval (stem->get_property ("beamlet-default-length"), Interval (1.1, 1.1));
505 Drul_array<Real> max_proportion
506 = robust_scm2interval (stem->get_property ("beamlet-max-length-proportion"), Interval (0.75, 0.75));
507 Real length = beamlet_length[seg.dir_];
511 Grob *neighbor_stem = stems[seg.stem_index_ + event_dir];
512 Real neighbor_stem_x = neighbor_stem->relative_coordinate (commonx, X_AXIS);
514 length = min (length,
515 fabs (neighbor_stem_x - seg.stem_x_) * max_proportion[seg.dir_]);
517 current.horizontal_[event_dir] += event_dir * length;
521 // we are examining the edge of a beam segment that is closest
522 // (ie. touching, unless there is a gap) its stem.
524 current.horizontal_[event_dir] += event_dir * seg.width_ / 2;
527 current.horizontal_[event_dir] -= event_dir * gap_length;
529 if (Stem::is_invisible (seg.stem_))
532 Need to do this in case of whole notes. We don't want the
533 heads to collide with the beams.
535 extract_grob_set (seg.stem_, "note-heads", heads);
537 for (vsize k = 0; k < heads.size (); k++)
538 current.horizontal_[event_dir]
539 = event_dir * min (event_dir * current.horizontal_[event_dir],
542 * heads[k]->extent (commonx,
543 X_AXIS)[-event_dir]);
548 if (event_dir == RIGHT)
550 segments.push_back (current);
551 current = Beam_segment ();
558 SCM segments_scm = SCM_EOL;
560 for (vsize i = segments.size (); i--;)
562 segments_scm = scm_cons (scm_list_2 (scm_cons (ly_symbol2scm ("vertical-count"),
563 scm_from_int (segments[i].vertical_count_)),
564 scm_cons (ly_symbol2scm ("horizontal"),
565 ly_interval2scm (segments[i].horizontal_))),
572 MAKE_SCHEME_CALLBACK (Beam, calc_x_positions, 1);
574 Beam::calc_x_positions (SCM smob)
576 Spanner *me = unsmob<Spanner> (smob);
577 SCM segments = me->get_property ("beam-segments");
578 Interval x_positions;
579 x_positions.set_empty ();
580 for (SCM s = segments; scm_is_pair (s); s = scm_cdr (s))
581 x_positions.unite (robust_scm2interval (ly_assoc_get (ly_symbol2scm ("horizontal"),
584 Interval (0.0, 0.0)));
586 // Case for beams without segments (i.e. uniting two skips with a beam)
587 // TODO: should issue a warning? warning likely issued downstream, but couldn't hurt...
588 if (x_positions.is_empty ())
590 extract_grob_set (me, "stems", stems);
591 Grob *common_x = common_refpoint_of_array (stems, me, X_AXIS);
592 for (LEFT_and_RIGHT (d))
593 x_positions[d] = me->relative_coordinate (common_x, X_AXIS);
595 return ly_interval2scm (x_positions);
599 Beam::get_beam_segments (Grob *me)
601 SCM segments_scm = me->get_property ("beam-segments");
602 vector<Beam_segment> segments;
603 for (SCM s = segments_scm; scm_is_pair (s); s = scm_cdr (s))
605 segments.push_back (Beam_segment ());
606 segments.back ().vertical_count_ = robust_scm2int (ly_assoc_get (ly_symbol2scm ("vertical-count"), scm_car (s), SCM_EOL), 0);
607 segments.back ().horizontal_ = robust_scm2interval (ly_assoc_get (ly_symbol2scm ("horizontal"), scm_car (s), SCM_EOL), Interval (0.0, 0.0));
613 MAKE_SCHEME_CALLBACK (Beam, print, 1);
615 Beam::print (SCM grob)
617 Spanner *me = unsmob<Spanner> (grob);
619 TODO - mild code dup for all the commonx calls.
620 Some use just common_refpoint_of_array, some (in print and
621 calc_beam_segments) use this plus calls to get_bound.
623 Figure out if there is any particular reason for this and
624 consolidate in one Beam::get_common function.
626 extract_grob_set (me, "stems", stems);
627 Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
628 for (LEFT_and_RIGHT (d))
629 commonx = me->get_bound (d)->common_refpoint (commonx, X_AXIS);
631 vector<Beam_segment> segments = get_beam_segments (me);
633 if (!segments.size ())
636 Real blot = me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
638 SCM posns = me->get_property ("quantized-positions");
639 Interval span = robust_scm2interval (me->get_property ("X-positions"), Interval (0, 0));
641 if (!is_number_pair (posns))
643 programming_error ("no beam positions?");
644 pos = Interval (0, 0);
647 pos = ly_scm2realdrul (posns);
649 scale_drul (&pos, Staff_symbol_referencer::staff_space (me));
651 Real dy = pos[RIGHT] - pos[LEFT];
652 Real slope = (dy && span.length ()) ? dy / span.length () : 0;
654 Real beam_thickness = get_beam_thickness (me);
655 Real beam_dy = get_beam_translation (me);
657 Direction feather_dir = to_dir (me->get_property ("grow-direction"));
659 Interval placements = robust_scm2interval (me->get_property ("normalized-endpoints"), Interval (0.0, 0.0));
662 vsize extreme = (segments[0].vertical_count_ == 0
663 ? segments[0].vertical_count_
664 : segments.back ().vertical_count_);
666 for (vsize i = 0; i < segments.size (); i++)
668 Real local_slope = slope;
670 Makes local slope proportional to the ratio of the length of this beam
674 local_slope += (feather_dir * segments[i].vertical_count_
676 * placements.length ()
679 Stencil b = Lookup::beam (local_slope, segments[i].horizontal_.length (), beam_thickness, blot);
681 b.translate_axis (segments[i].horizontal_[LEFT], X_AXIS);
682 Real multiplier = feather_dir ? placements[LEFT] : 1.0;
684 Interval weights (1 - multiplier, multiplier);
686 if (feather_dir != LEFT)
689 // we need two translations: the normal one and
690 // the one of the lowest segment
691 size_t idx[] = {i, extreme};
692 Real translations[2];
694 for (int j = 0; j < 2; j++)
695 translations[j] = slope
696 * (segments[idx[j]].horizontal_[LEFT] - span.linear_combination (CENTER))
697 + pos.linear_combination (CENTER)
698 + beam_dy * segments[idx[j]].vertical_count_;
700 Real weighted_average = translations[0] * weights[LEFT] + translations[1] * weights[RIGHT];
703 Tricky. The manipulation of the variable `weighted_average' below ensures
704 that beams with a RIGHT grow direction will start from the position of the
705 lowest segment at 0, and this error will decrease and decrease over the
706 course of the beam. Something with a LEFT grow direction, on the other
707 hand, will always start in the correct place but progressively accrue
708 error at broken places. This code shifts beams up given where they are
709 in the total span length (controlled by the variable `multiplier'). To
710 better understand what it does, try commenting it out: you'll see that
711 all of the RIGHT growing beams immediately start too low and get better
712 over line breaks, whereas all of the LEFT growing beams start just right
713 and get worse over line breaks.
715 Real factor = Interval (multiplier, 1 - multiplier).linear_combination (feather_dir);
717 if (segments[0].vertical_count_ < 0 && feather_dir)
718 weighted_average += beam_dy * (segments.size () - 1) * factor;
720 b.translate_axis (weighted_average, Y_AXIS);
722 the_beam.add_stencil (b);
726 #if (DEBUG_BEAM_SCORING)
727 SCM annotation = me->get_property ("annotation");
728 if (scm_is_string (annotation))
730 extract_grob_set (me, "stems", stems);
733 This code prints the demerits for each beam. Perhaps this
734 should be switchable for those who want to twiddle with the
738 SCM properties = Font_interface::text_font_alist_chain (me);
740 properties = scm_cons (scm_acons (ly_symbol2scm ("font-size"), scm_from_int (-5), SCM_EOL),
743 Direction stem_dir = stems.size () ? to_dir (stems[0]->get_property ("direction")) : UP;
745 Stencil score = *unsmob<Stencil> (Text_interface::interpret_markup
746 (me->layout ()->self_scm (), properties, annotation));
748 if (!score.is_empty ())
750 score.translate_axis (me->relative_coordinate (commonx, X_AXIS), X_AXIS);
751 the_beam.add_at_edge (Y_AXIS, stem_dir, score, 1.0);
756 the_beam.translate_axis (-me->relative_coordinate (commonx, X_AXIS), X_AXIS);
757 return the_beam.smobbed_copy ();
761 Beam::get_default_dir (Grob *me)
763 extract_grob_set (me, "stems", stems);
765 Drul_array<Real> extremes (0.0, 0.0);
766 for (iterof (s, stems); s != stems.end (); s++)
768 Interval positions = Stem::head_positions (*s);
769 for (DOWN_and_UP (d))
771 if (sign (positions[d]) == d)
772 extremes[d] = d * max (d * positions[d], d * extremes[d]);
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))
864 vector<Interval> forbidden_intervals;
866 extract_grob_set (me, "normal-stems", stems);
868 Grob *common = common_refpoint_of_array (stems, me, Y_AXIS);
869 Real staff_space = Staff_symbol_referencer::staff_space (me);
871 vector<Interval> head_extents_array;
872 for (vsize i = 0; i < stems.size (); i++)
874 Grob *stem = stems[i];
876 Interval head_extents;
877 if (Stem::head_count (stem))
879 head_extents = Stem::head_positions (stem);
880 head_extents.widen (1);
881 head_extents *= staff_space * 0.5;
884 We could subtract beam Y position, but this routine only
885 sets stem directions, a constant shift does not have an
888 head_extents += stem->pure_relative_y_coordinate (common, 0, INT_MAX);
890 if (to_dir (stem->get_property_data ("direction")))
892 Direction stemdir = to_dir (stem->get_property ("direction"));
893 head_extents[-stemdir] = -stemdir * infinity_f;
896 head_extents_array.push_back (head_extents);
898 forbidden_intervals.push_back (head_extents);
902 Real max_gap_len = 0.0;
904 vector<Interval> allowed_regions
905 = Interval_set::interval_union (forbidden_intervals).complement ().intervals ();
906 for (vsize i = allowed_regions.size () - 1; i != VPOS; i--)
908 Interval gap = 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 (scm_is_null (shorten_list))
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_is_true (ly_memv (scm_car (s), scm_cdr (beaming))))
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);
1136 for (vsize i = 0; i < stems.size (); i++)
1139 Don't overwrite user settings.
1141 for (LEFT_and_RIGHT (d))
1143 Grob *stem = stems[i];
1144 SCM beaming_prop = stem->get_property ("beaming");
1145 if (scm_is_null (beaming_prop)
1146 || scm_is_null (index_get_cell (beaming_prop, d)))
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);
1167 Beam::forced_stem_count (Grob *me)
1169 extract_grob_set (me, "normal-stems", stems);
1172 for (vsize i = 0; i < stems.size (); i++)
1176 /* I can imagine counting those boundaries as a half forced stem,
1177 but let's count them full for now. */
1178 Direction defdir = to_dir (s->get_property ("default-direction"));
1180 if (abs (Stem::chord_start_y (s)) > 0.1
1182 && get_grob_direction (s) != defdir)
1189 Beam::normal_stem_count (Grob *me)
1191 extract_grob_set (me, "normal-stems", stems);
1192 return stems.size ();
1196 Beam::first_normal_stem (Grob *me)
1198 extract_grob_set (me, "normal-stems", stems);
1199 return stems.size () ? stems[0] : 0;
1203 Beam::last_normal_stem (Grob *me)
1205 extract_grob_set (me, "normal-stems", stems);
1206 return stems.size () ? stems.back () : 0;
1212 handle rest under beam (do_post: beams are calculated now)
1213 what about combination of collisions and rest under beam.
1217 rest -> stem -> beam -> interpolate_y_position ()
1219 MAKE_SCHEME_CALLBACK_WITH_OPTARGS (Beam, rest_collision_callback, 2, 1, "");
1221 Beam::rest_collision_callback (SCM smob, SCM prev_offset)
1223 Grob *rest = unsmob<Grob> (smob);
1224 if (scm_is_number (rest->get_property ("staff-position")))
1225 return scm_from_int (0);
1227 Real offset = robust_scm2double (prev_offset, 0.0);
1229 Grob *st = unsmob<Grob> (rest->get_object ("stem"));
1232 return scm_from_double (0.0);
1233 Grob *beam = unsmob<Grob> (stem->get_object ("beam"));
1235 || !has_interface<Beam> (beam)
1236 || !Beam::normal_stem_count (beam))
1237 return scm_from_double (0.0);
1239 Grob *common_y = rest->common_refpoint (beam, Y_AXIS);
1241 Drul_array<Real> pos (robust_scm2drul (beam->get_property ("positions"),
1242 Drul_array<Real> (0, 0)));
1244 for (LEFT_and_RIGHT (dir))
1245 pos[dir] += beam->relative_coordinate (common_y, Y_AXIS);
1247 Real staff_space = Staff_symbol_referencer::staff_space (rest);
1249 scale_drul (&pos, staff_space);
1251 Real dy = pos[RIGHT] - pos[LEFT];
1253 extract_grob_set (beam, "stems", stems);
1254 Grob *common = common_refpoint_of_array (stems, beam, X_AXIS);
1256 Interval x_span = robust_scm2interval (beam->get_property ("X-positions"),
1257 Interval (0.0, 0.0));
1258 Real x0 = x_span[LEFT];
1259 Real dx = x_span.length ();
1260 Real slope = dy && dx ? dy / dx : 0;
1262 Direction d = get_grob_direction (stem);
1263 Real stem_y = pos[LEFT]
1264 + (stem->relative_coordinate (common, X_AXIS) - x0) * slope;
1266 Real beam_translation = get_beam_translation (beam);
1267 Real beam_thickness = Beam::get_beam_thickness (beam);
1270 TODO: this is not strictly correct for 16th knee beams.
1273 = Stem::beam_multiplicity (stem).length () + 1;
1275 Real height_of_my_beams = beam_thickness / 2
1276 + (beam_count - 1) * beam_translation;
1277 Real beam_y = stem_y - d * height_of_my_beams;
1279 Interval rest_extent = rest->extent (rest, Y_AXIS);
1280 rest_extent.translate (offset + rest->get_parent (Y_AXIS)->relative_coordinate (common_y, Y_AXIS));
1282 Real rest_dim = rest_extent[d];
1283 Real minimum_distance
1284 = staff_space * (robust_scm2double (stem->get_property ("stemlet-length"), 0.0)
1285 + robust_scm2double (rest->get_property ("minimum-distance"), 0.0));
1287 Real shift = d * min (d * (beam_y - d * minimum_distance - rest_dim), 0.0);
1289 shift /= staff_space;
1291 /* Always move discretely by half spaces */
1292 shift = ceil (fabs (shift * 2.0)) / 2.0 * sign (shift);
1294 Interval staff_span = Staff_symbol_referencer::staff_span (rest);
1295 staff_span *= staff_space / 2;
1297 /* Inside staff, move by whole spaces*/
1298 if (staff_span.contains (rest_extent[d] + staff_space * shift)
1299 || staff_span.contains (rest_extent[-d] + staff_space * shift))
1300 shift = ceil (fabs (shift)) * sign (shift);
1302 return scm_from_double (offset + staff_space * shift);
1306 Estimate the position of a rest under a beam,
1307 using the average position of its neighboring heads.
1309 MAKE_SCHEME_CALLBACK_WITH_OPTARGS (Beam, pure_rest_collision_callback, 4, 1, "");
1311 Beam::pure_rest_collision_callback (SCM smob,
1316 Real previous = robust_scm2double (prev_offset, 0.0);
1318 Grob *me = unsmob<Grob> (smob);
1319 Grob *stem = unsmob<Grob> (me->get_object ("stem"));
1321 return scm_from_double (previous);
1322 Grob *beam = unsmob<Grob> (stem->get_object ("beam"));
1324 || !Beam::normal_stem_count (beam)
1325 || !is_direction (beam->get_property_data ("direction")))
1326 return scm_from_double (previous);
1328 Real ss = Staff_symbol_referencer::staff_space (me);
1330 extract_grob_set (beam, "stems", stems);
1331 vector<Grob *> my_stems;
1333 for (vsize i = 0; i < stems.size (); i++)
1334 if (Stem::head_count (stems[i]) || stems[i] == stem)
1335 my_stems.push_back (stems[i]);
1339 for (vsize i = 0; i < my_stems.size (); i++)
1340 if (my_stems[i] == stem)
1348 if (idx == (vsize) - 1 || my_stems.size () == 1)
1349 return scm_from_double (previous);
1351 left = right = my_stems[1];
1352 else if (idx == my_stems.size () - 1)
1353 left = right = my_stems[idx - 1];
1356 left = my_stems[idx - 1];
1357 right = my_stems[idx + 1];
1360 /* Estimate the closest beam to be four positions away from the heads, */
1361 Direction beamdir = get_grob_direction (beam);
1362 Real beam_pos = (Stem::head_positions (left)[beamdir]
1363 + Stem::head_positions (right)[beamdir]) / 2.0
1364 + 4.0 * beamdir; // four staff-positions
1365 /* and that the closest beam never crosses staff center by more than two positions */
1366 beam_pos = max (-2.0, beam_pos * beamdir) * beamdir;
1368 Real minimum_distance
1369 = ss * (robust_scm2double (stem->get_property ("stemlet-length"), 0.0)
1370 + robust_scm2double (me->get_property ("minimum-distance"), 0.0));
1371 Real offset = beam_pos * ss / 2.0
1372 - minimum_distance * beamdir
1373 - me->extent (me, Y_AXIS)[beamdir];
1375 /* Always move by a whole number of staff spaces, always away from the beam */
1376 offset = floor (min (0.0, (offset - previous) / ss * beamdir))
1377 * ss * beamdir + previous;
1379 return scm_from_double (offset);
1383 Beam::is_knee (Grob *me)
1385 SCM k = me->get_property ("knee");
1386 if (scm_is_bool (k))
1387 return ly_scm2bool (k);
1391 extract_grob_set (me, "stems", stems);
1392 for (vsize i = stems.size (); i--;)
1394 Direction dir = get_grob_direction (stems[i]);
1403 me->set_property ("knee", ly_bool2scm (knee));
1409 Beam::is_cross_staff (Grob *me)
1411 extract_grob_set (me, "stems", stems);
1412 Grob *staff_symbol = Staff_symbol_referencer::get_staff_symbol (me);
1413 for (vsize i = 0; i < stems.size (); i++)
1414 if (Staff_symbol_referencer::get_staff_symbol (stems[i]) != staff_symbol)
1419 MAKE_SCHEME_CALLBACK (Beam, calc_cross_staff, 1)
1421 Beam::calc_cross_staff (SCM smob)
1423 return scm_from_bool (is_cross_staff (unsmob<Grob> (smob)));
1427 Beam::get_direction_beam_count (Grob *me, Direction d)
1429 extract_grob_set (me, "stems", stems);
1432 for (vsize i = stems.size (); i--;)
1435 Should we take invisible stems into account?
1437 if (get_grob_direction (stems[i]) == d)
1438 bc = max (bc, (Stem::beam_multiplicity (stems[i]).length () + 1));
1444 ADD_INTERFACE (Beam,
1447 "The @code{beam-thickness} property is the weight of beams,"
1448 " measured in staffspace. The @code{direction} property is"
1449 " not user-serviceable. Use the @code{direction} property"
1450 " of @code{Stem} instead.\n"
1451 "The following properties may be set in the @code{details}"
1455 "@item stem-length-demerit-factor\n"
1456 "Demerit factor used for inappropriate stem lengths.\n"
1457 "@item secondary-beam-demerit\n"
1458 "Demerit used in quanting calculations for multiple"
1460 "@item region-size\n"
1461 "Size of region for checking quant scores.\n"
1463 "Epsilon for beam quant code to check for presence"
1465 "@item stem-length-limit-penalty\n"
1466 "Penalty for differences in stem lengths on a beam.\n"
1467 "@item damping-direction-penalty\n"
1468 "Demerit penalty applied when beam direction is different"
1469 " from damping direction.\n"
1470 "@item hint-direction-penalty\n"
1471 "Demerit penalty applied when beam direction is different"
1472 " from damping direction, but damping slope is"
1473 " <= @code{round-to-zero-slope}.\n"
1474 "@item musical-direction-factor\n"
1475 "Demerit scaling factor for difference between"
1476 " beam slope and music slope.\n"
1477 "@item ideal-slope-factor\n"
1478 "Demerit scaling factor for difference between"
1479 " beam slope and damping slope.\n"
1480 "@item round-to-zero-slope\n"
1481 "Damping slope which is considered zero for purposes of"
1482 " calculating direction penalties.\n"
1488 "beamed-stem-shorten "
1495 "collision-interfaces "
1496 "collision-voice-only "
1508 "neutral-direction "
1511 "quantized-positions "