2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 1997--2011 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 irrelevent because it will
470 // be connected with the next segment in the event_dir direction.
473 current.vertical_count_ = vertical_count;
474 current.horizontal_[event_dir] = seg.stem_x_;
475 if (seg.dir_ == event_dir)
476 // then we are examining the edge of a beam segment that is furthest
480 && me->get_bound (event_dir)->break_status_dir ())
482 current.horizontal_[event_dir]
483 = (robust_relative_extent (me->get_bound (event_dir),
484 commonx, X_AXIS)[RIGHT]
485 + event_dir * break_overshoot[event_dir]);
489 Grob *stem = stems[seg.stem_index_];
490 Drul_array<Real> beamlet_length
491 = robust_scm2interval (stem->get_property ("beamlet-default-length"), Interval (1.1, 1.1));
492 Drul_array<Real> max_proportion
493 = robust_scm2interval (stem->get_property ("beamlet-max-length-proportion"), Interval (0.75, 0.75));
494 Real length = beamlet_length[seg.dir_];
498 Grob *neighbor_stem = stems[seg.stem_index_ + event_dir];
499 Real neighbor_stem_x = neighbor_stem->relative_coordinate (commonx, X_AXIS);
501 length = min (length,
502 fabs (neighbor_stem_x - seg.stem_x_) * max_proportion[seg.dir_]);
504 current.horizontal_[event_dir] += event_dir * length;
508 // we are examining the edge of a beam segment that is closest
509 // (ie. touching, unless there is a gap) its stem.
511 current.horizontal_[event_dir] += event_dir * seg.width_ / 2;
514 current.horizontal_[event_dir] -= event_dir * gap_length;
516 if (Stem::is_invisible (seg.stem_))
519 Need to do this in case of whole notes. We don't want the
520 heads to collide with the beams.
522 extract_grob_set (seg.stem_, "note-heads", heads);
524 for (vsize k = 0; k < heads.size (); k++)
525 current.horizontal_[event_dir]
526 = event_dir * min (event_dir * current.horizontal_[event_dir],
529 * heads[k]->extent (commonx,
530 X_AXIS)[-event_dir]);
535 if (event_dir == RIGHT)
537 segments.push_back (current);
538 current = Beam_segment ();
541 while (flip (&event_dir) != LEFT);
546 SCM segments_scm = SCM_EOL;
547 SCM *tail = &segments_scm;
549 for (vsize i = 0; i < segments.size (); i++)
551 *tail = scm_cons (scm_list_2 (scm_cons (ly_symbol2scm ("vertical-count"),
552 scm_from_int (segments[i].vertical_count_)),
553 scm_cons (ly_symbol2scm ("horizontal"),
554 ly_interval2scm (segments[i].horizontal_))),
556 tail = SCM_CDRLOC (*tail);
562 MAKE_SCHEME_CALLBACK (Beam, calc_x_positions, 1);
564 Beam::calc_x_positions (SCM smob)
566 Spanner *me = unsmob_spanner (smob);
567 SCM segments = me->get_property ("beam-segments");
568 Interval x_positions;
569 x_positions.set_empty ();
570 for (SCM s = segments; scm_is_pair (s); s = scm_cdr (s))
571 x_positions.unite (robust_scm2interval (ly_assoc_get (ly_symbol2scm ("horizontal"),
574 Interval (0.0, 0.0)));
576 // Case for beams without segments (i.e. uniting two skips with a beam)
577 // TODO: should issue a warning? warning likely issued downstream, but couldn't hurt...
578 if (x_positions.is_empty ())
580 extract_grob_set (me, "stems", stems);
581 Grob *common_x = common_refpoint_of_array (stems, me, X_AXIS);
584 x_positions[d] = me->relative_coordinate (common_x, X_AXIS);
585 while (flip (&d) != LEFT);
587 return ly_interval2scm (x_positions);
591 Beam::get_beam_segments (Grob *me)
593 SCM segments_scm = me->get_property ("beam-segments");
594 vector<Beam_segment> segments;
595 for (SCM s = segments_scm; scm_is_pair (s); s = scm_cdr (s))
597 segments.push_back (Beam_segment ());
598 segments.back ().vertical_count_ = robust_scm2int (ly_assoc_get (ly_symbol2scm ("vertical-count"), scm_car (s), SCM_EOL), 0);
599 segments.back ().horizontal_ = robust_scm2interval (ly_assoc_get (ly_symbol2scm ("horizontal"), scm_car (s), SCM_EOL), Interval (0.0, 0.0));
605 MAKE_SCHEME_CALLBACK (Beam, print, 1);
607 Beam::print (SCM grob)
609 Spanner *me = unsmob_spanner (grob);
611 TODO - mild code dup for all the commonx calls.
612 Some use just common_refpoint_of_array, some (in print and
613 calc_beam_segments) use this plus calls to get_bound.
615 Figure out if there is any particular reason for this and
616 consolidate in one Beam::get_common function.
618 extract_grob_set (me, "stems", stems);
619 Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
622 commonx = me->get_bound (d)->common_refpoint (commonx, X_AXIS);
623 while (flip (&d) != LEFT);
625 vector<Beam_segment> segments = get_beam_segments (me);
627 if (!segments.size ())
630 Real blot = me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
632 SCM posns = me->get_property ("quantized-positions");
633 Interval span = robust_scm2interval (me->get_property ("X-positions"), Interval (0, 0));
635 if (!is_number_pair (posns))
637 programming_error ("no beam positions?");
638 pos = Interval (0, 0);
641 pos = ly_scm2realdrul (posns);
643 scale_drul (&pos, Staff_symbol_referencer::staff_space (me));
645 Real dy = pos[RIGHT] - pos[LEFT];
646 Real slope = (dy && span.length ()) ? dy / span.length () : 0;
648 Real beam_thickness = get_beam_thickness (me);
649 Real beam_dy = get_beam_translation (me);
651 Direction feather_dir = to_dir (me->get_property ("grow-direction"));
653 Interval placements = robust_scm2interval (me->get_property ("normalized-endpoints"), Interval (0.0, 0.0));
656 int extreme = (segments[0].vertical_count_ == 0
657 ? segments[0].vertical_count_
658 : segments.back ().vertical_count_);
660 for (vsize i = 0; i < segments.size (); i++)
662 Real local_slope = slope;
664 Makes local slope proportional to the ratio of the length of this beam
668 local_slope += (feather_dir * segments[i].vertical_count_
670 * placements.length ()
673 Stencil b = Lookup::beam (local_slope, segments[i].horizontal_.length (), beam_thickness, blot);
675 b.translate_axis (segments[i].horizontal_[LEFT], X_AXIS);
676 Real multiplier = feather_dir ? placements[LEFT] : 1.0;
678 Interval weights (1 - multiplier, multiplier);
680 if (feather_dir != LEFT)
683 // we need two translations: the normal one and
684 // the one of the lowest segment
685 int idx[] = {i, extreme};
686 Real translations[2];
688 for (int j = 0; j < 2; j++)
689 translations[j] = slope
690 * (segments[idx[j]].horizontal_[LEFT] - span.linear_combination (CENTER))
691 + pos.linear_combination (CENTER)
692 + beam_dy * segments[idx[j]].vertical_count_;
694 Real weighted_average = translations[0] * weights[LEFT] + translations[1] * weights[RIGHT];
697 Tricky. The manipulation of the variable `weighted_average' below ensures
698 that beams with a RIGHT grow direction will start from the position of the
699 lowest segment at 0, and this error will decrease and decrease over the
700 course of the beam. Something with a LEFT grow direction, on the other
701 hand, will always start in the correct place but progressively accrue
702 error at broken places. This code shifts beams up given where they are
703 in the total span length (controlled by the variable `multiplier'). To
704 better understand what it does, try commenting it out: you'll see that
705 all of the RIGHT growing beams immediately start too low and get better
706 over line breaks, whereas all of the LEFT growing beams start just right
707 and get worse over line breaks.
709 Real factor = Interval (multiplier, 1 - multiplier).linear_combination (feather_dir);
711 if (segments[0].vertical_count_ < 0 && feather_dir)
712 weighted_average += beam_dy * (segments.size () - 1) * factor;
714 b.translate_axis (weighted_average, Y_AXIS);
716 the_beam.add_stencil (b);
720 #if (DEBUG_BEAM_SCORING)
721 SCM annotation = me->get_property ("annotation");
722 if (scm_is_string (annotation))
724 extract_grob_set (me, "stems", stems);
727 This code prints the demerits for each beam. Perhaps this
728 should be switchable for those who want to twiddle with the
732 SCM properties = Font_interface::text_font_alist_chain (me);
734 properties = scm_cons (scm_acons (ly_symbol2scm ("font-size"), scm_from_int (-5), SCM_EOL),
737 Direction stem_dir = stems.size () ? to_dir (stems[0]->get_property ("direction")) : UP;
739 Stencil score = *unsmob_stencil (Text_interface::interpret_markup
740 (me->layout ()->self_scm (), properties, annotation));
742 if (!score.is_empty ())
744 score.translate_axis (me->relative_coordinate (commonx, X_AXIS), X_AXIS);
745 the_beam.add_at_edge (Y_AXIS, stem_dir, score, 1.0);
750 the_beam.translate_axis (-me->relative_coordinate (commonx, X_AXIS), X_AXIS);
751 return the_beam.smobbed_copy ();
755 Beam::get_default_dir (Grob *me)
757 extract_grob_set (me, "stems", stems);
759 Drul_array<Real> extremes (0.0, 0.0);
760 for (iterof (s, stems); s != stems.end (); s++)
762 Interval positions = Stem::head_positions (*s);
766 if (sign (positions[d]) == d)
767 extremes[d] = d * max (d * positions[d], d * extremes[d]);
769 while (flip (&d) != DOWN);
772 Drul_array<int> total (0, 0);
773 Drul_array<int> count (0, 0);
775 bool force_dir = false;
776 for (vsize i = 0; i < stems.size (); i++)
779 Direction stem_dir = CENTER;
780 SCM stem_dir_scm = s->get_property_data ("direction");
781 if (is_direction (stem_dir_scm))
783 stem_dir = to_dir (stem_dir_scm);
787 stem_dir = to_dir (s->get_property ("default-direction"));
790 stem_dir = to_dir (s->get_property ("neutral-direction"));
795 total[stem_dir] += max (int (- stem_dir * Stem::head_positions (s) [-stem_dir]), 0);
801 if (abs (extremes[UP]) > -extremes[DOWN])
803 else if (extremes[UP] < -extremes[DOWN])
807 Direction dir = CENTER;
808 Direction d = CENTER;
809 if ((d = (Direction) sign (count[UP] - count[DOWN])))
813 && (d = (Direction) sign (total[UP] / count[UP] - total[DOWN] / count[DOWN])))
815 else if ((d = (Direction) sign (total[UP] - total[DOWN])))
818 dir = to_dir (me->get_property ("neutral-direction"));
823 /* Set all stems with non-forced direction to beam direction.
824 Urg: non-forced should become `without/with unforced' direction,
825 once stem gets cleaned-up. */
827 Beam::set_stem_directions (Grob *me, Direction d)
829 extract_grob_set (me, "stems", stems);
831 for (vsize i = 0; i < stems.size (); i++)
835 SCM forcedir = s->get_property_data ("direction");
836 if (!to_dir (forcedir))
837 set_grob_direction (s, d);
842 Only try horizontal beams for knees. No reliable detection of
843 anything else is possible here, since we don't know funky-beaming
844 settings, or X-distances (slopes!) People that want sloped
845 knee-beams, should set the directions manually.
850 this routine should take into account the stemlength scoring
851 of a possible knee/nonknee beam.
854 Beam::consider_auto_knees (Grob *me)
856 SCM scm = me->get_property ("auto-knee-gap");
857 if (!scm_is_number (scm))
864 extract_grob_set (me, "normal-stems", stems);
866 Grob *common = common_refpoint_of_array (stems, me, Y_AXIS);
867 Real staff_space = Staff_symbol_referencer::staff_space (me);
869 vector<Interval> head_extents_array;
870 for (vsize i = 0; i < stems.size (); i++)
872 Grob *stem = stems[i];
874 Interval head_extents = Stem::head_positions (stem);
875 if (!head_extents.is_empty ())
877 head_extents[LEFT] += -1;
878 head_extents[RIGHT] += 1;
879 head_extents *= staff_space * 0.5;
882 We could subtract beam Y position, but this routine only
883 sets stem directions, a constant shift does not have an
886 head_extents += stem->pure_relative_y_coordinate (common, 0, INT_MAX);
888 if (to_dir (stem->get_property_data ("direction")))
890 Direction stemdir = to_dir (stem->get_property ("direction"));
891 head_extents[-stemdir] = -stemdir * infinity_f;
894 head_extents_array.push_back (head_extents);
896 gaps.remove_interval (head_extents);
900 Real max_gap_len = 0.0;
902 for (vsize i = gaps.allowed_regions_.size () - 1; i != VPOS; i--)
904 Interval gap = gaps.allowed_regions_[i];
907 the outer gaps are not knees.
909 if (isinf (gap[LEFT]) || isinf (gap[RIGHT]))
912 if (gap.length () >= max_gap_len)
914 max_gap_len = gap.length ();
919 Real beam_translation = get_beam_translation (me);
920 Real beam_thickness = Beam::get_beam_thickness (me);
921 int beam_count = Beam::get_beam_count (me);
922 Real height_of_beams = beam_thickness / 2
923 + (beam_count - 1) * beam_translation;
924 Real threshold = scm_to_double (scm) + height_of_beams;
926 if (max_gap_len > threshold)
929 for (vsize i = 0; i < stems.size (); i++)
931 Grob *stem = stems[i];
932 Interval head_extents = head_extents_array[j++];
934 Direction d = (head_extents.center () < max_gap.center ())
937 stem->set_property ("direction", scm_from_int (d));
939 head_extents.intersect (max_gap);
940 assert (head_extents.is_empty () || head_extents.length () < 1e-6);
945 MAKE_SCHEME_CALLBACK (Beam, calc_stem_shorten, 1)
947 Beam::calc_stem_shorten (SCM smob)
949 Grob *me = unsmob_grob (smob);
952 shortening looks silly for x staff beams
955 return scm_from_int (0);
957 Real forced_fraction = 1.0 * forced_stem_count (me)
958 / normal_stem_count (me);
960 int beam_count = get_beam_count (me);
962 SCM shorten_list = me->get_property ("beamed-stem-shorten");
963 if (shorten_list == SCM_EOL)
964 return scm_from_int (0);
966 Real staff_space = Staff_symbol_referencer::staff_space (me);
969 = robust_list_ref (beam_count - 1, shorten_list);
970 Real shorten = scm_to_double (shorten_elt) * staff_space;
972 shorten *= forced_fraction;
975 return scm_from_double (shorten);
977 return scm_from_double (0.0);
980 MAKE_SCHEME_CALLBACK (Beam, quanting, 3);
982 Beam::quanting (SCM smob, SCM ys_scm, SCM align_broken_intos)
984 Grob *me = unsmob_grob (smob);
985 Drul_array<Real> ys = robust_scm2drul (ys_scm, Drul_array<Real> (infinity_f, -infinity_f));
986 bool cbs = to_boolean (align_broken_intos);
988 Beam_scoring_problem problem (me, ys, cbs);
989 ys = problem.solve ();
991 return ly_interval2scm (ys);
995 Report slice containing the numbers that are both in (car BEAMING)
999 where_are_the_whole_beams (SCM beaming)
1003 for (SCM s = scm_car (beaming); scm_is_pair (s); s = scm_cdr (s))
1005 if (scm_c_memq (scm_car (s), scm_cdr (beaming)) != SCM_BOOL_F)
1007 l.add_point (scm_to_int (scm_car (s)));
1013 /* Return the Y position of the stem-end, given the Y-left, Y-right
1014 in POS for stem S. This Y position is relative to S. */
1016 Beam::calc_stem_y (Grob *me, Grob *stem, Grob **common,
1017 Real xl, Real xr, Direction feather_dir,
1018 Drul_array<Real> pos, bool french)
1020 Real beam_translation = get_beam_translation (me);
1021 Direction stem_dir = get_grob_direction (stem);
1024 Real relx = dx ? (stem->relative_coordinate (common[X_AXIS], X_AXIS) - xl) / dx : 0;
1025 Real xdir = 2 * relx - 1;
1027 Real stem_y = linear_combination (pos, xdir);
1029 SCM beaming = stem->get_property ("beaming");
1031 Slice beam_slice (french
1032 ? where_are_the_whole_beams (beaming)
1033 : Stem::beam_multiplicity (stem));
1034 if (beam_slice.is_empty ())
1035 beam_slice = Slice (0, 0);
1036 Interval beam_multiplicity (beam_slice[LEFT],
1040 feather dir = 1 , relx 0->1 : factor 0 -> 1
1041 feather dir = 0 , relx 0->1 : factor 1 -> 1
1042 feather dir = -1, relx 0->1 : factor 1 -> 0
1044 Real feather_factor = 1;
1045 if (feather_dir > 0)
1046 feather_factor = relx;
1047 else if (feather_dir < 0)
1048 feather_factor = 1 - relx;
1050 stem_y += feather_factor * beam_translation
1051 * beam_multiplicity[Direction (((french) ? DOWN : UP) * stem_dir)];
1052 Real id = me->relative_coordinate (common[Y_AXIS], Y_AXIS)
1053 - stem->relative_coordinate (common[Y_AXIS], Y_AXIS);
1059 Hmm. At this time, beam position and slope are determined. Maybe,
1060 stem directions and length should set to relative to the chord's
1061 position of the beam. */
1062 MAKE_SCHEME_CALLBACK (Beam, set_stem_lengths, 1);
1064 Beam::set_stem_lengths (SCM smob)
1066 Grob *me = unsmob_grob (smob);
1068 /* trigger callbacks. */
1069 (void) me->get_property ("direction");
1070 (void) me->get_property ("beaming");
1072 SCM posns = me->get_property ("positions");
1074 extract_grob_set (me, "stems", stems);
1079 for (int a = 2; a--;)
1080 common[a] = common_refpoint_of_array (stems, me, Axis (a));
1082 Drul_array<Real> pos = ly_scm2realdrul (posns);
1083 Real staff_space = Staff_symbol_referencer::staff_space (me);
1084 scale_drul (&pos, staff_space);
1088 if (robust_scm2int (me->get_property ("gap-count"), 0))
1091 thick = get_beam_thickness (me);
1094 Grob *fvs = first_normal_stem (me);
1095 Grob *lvs = last_normal_stem (me);
1097 Interval x_span = robust_scm2interval (me->get_property ("X-positions"), Interval (0,0));
1098 Direction feather_dir = to_dir (me->get_property ("grow-direction"));
1100 for (vsize i = 0; i < stems.size (); i++)
1104 bool french = to_boolean (s->get_property ("french-beaming"));
1105 Real stem_y = calc_stem_y (me, s, common,
1106 x_span[LEFT], x_span[RIGHT], feather_dir,
1107 pos, french && s != lvs && s != fvs);
1110 Make the stems go up to the end of the beam. This doesn't matter
1111 for normal beams, but for tremolo beams it looks silly otherwise.
1114 && !Stem::is_invisible (s))
1115 stem_y += thick * 0.5 * get_grob_direction (s);
1118 Do set_stem_positions for invisible stems too, so tuplet brackets
1119 have a reference point for sloping
1121 Stem::set_stem_positions (s, 2 * stem_y / staff_space);
1128 Beam::set_beaming (Grob *me, Beaming_pattern const *beaming)
1130 extract_grob_set (me, "stems", stems);
1133 for (vsize i = 0; i < stems.size (); i++)
1136 Don't overwrite user settings.
1140 Grob *stem = stems[i];
1141 SCM beaming_prop = stem->get_property ("beaming");
1142 if (beaming_prop == SCM_EOL
1143 || index_get_cell (beaming_prop, d) == SCM_EOL)
1145 int count = beaming->beamlet_count (i, d);
1147 && i + 1 < stems.size ()
1148 && Stem::is_invisible (stem))
1149 count = min (count, beaming->beamlet_count (i, -d));
1151 if ( ((i == 0 && d == LEFT)
1152 || (i == stems.size () - 1 && d == RIGHT))
1153 && stems.size () > 1
1154 && to_boolean (me->get_property ("clip-edges")))
1157 Stem::set_beaming (stem, count, d);
1160 while (flip (&d) != LEFT);
1165 Beam::forced_stem_count (Grob *me)
1167 extract_grob_set (me, "normal-stems", stems);
1170 for (vsize i = 0; i < stems.size (); i++)
1174 /* I can imagine counting those boundaries as a half forced stem,
1175 but let's count them full for now. */
1176 Direction defdir = to_dir (s->get_property ("default-direction"));
1178 if (abs (Stem::chord_start_y (s)) > 0.1
1180 && get_grob_direction (s) != defdir)
1187 Beam::normal_stem_count (Grob *me)
1189 extract_grob_set (me, "normal-stems", stems);
1190 return stems.size ();
1194 Beam::first_normal_stem (Grob *me)
1196 extract_grob_set (me, "normal-stems", stems);
1197 return stems.size () ? stems[0] : 0;
1201 Beam::last_normal_stem (Grob *me)
1203 extract_grob_set (me, "normal-stems", stems);
1204 return stems.size () ? stems.back () : 0;
1210 handle rest under beam (do_post: beams are calculated now)
1211 what about combination of collisions and rest under beam.
1215 rest -> stem -> beam -> interpolate_y_position ()
1217 MAKE_SCHEME_CALLBACK_WITH_OPTARGS (Beam, rest_collision_callback, 2, 1, "");
1219 Beam::rest_collision_callback (SCM smob, SCM prev_offset)
1221 Grob *rest = unsmob_grob (smob);
1222 if (scm_is_number (rest->get_property ("staff-position")))
1223 return scm_from_int (0);
1225 Real offset = robust_scm2double (prev_offset, 0.0);
1227 Grob *st = unsmob_grob (rest->get_object ("stem"));
1230 return scm_from_double (0.0);
1231 Grob *beam = unsmob_grob (stem->get_object ("beam"));
1233 || !Beam::has_interface (beam)
1234 || !Beam::normal_stem_count (beam))
1235 return scm_from_double (0.0);
1237 Drul_array<Real> pos (robust_scm2drul (beam->get_property ("positions"),
1238 Drul_array<Real> (0, 0)));
1240 Real staff_space = Staff_symbol_referencer::staff_space (rest);
1242 scale_drul (&pos, staff_space);
1244 Real dy = pos[RIGHT] - pos[LEFT];
1246 extract_grob_set (beam, "stems", stems);
1247 Grob *common = common_refpoint_of_array (stems, beam, X_AXIS);
1249 Interval x_span = robust_scm2interval (beam->get_property ("X-positions"),
1250 Interval (0.0, 0.0));
1251 Real x0 = x_span[LEFT];
1252 Real dx = x_span.length ();
1253 Real slope = dy && dx ? dy / dx : 0;
1255 Direction d = get_grob_direction (stem);
1256 Real stem_y = pos[LEFT]
1257 + (stem->relative_coordinate (common, X_AXIS) - x0) * slope;
1259 Real beam_translation = get_beam_translation (beam);
1260 Real beam_thickness = Beam::get_beam_thickness (beam);
1263 TODO: this is not strictly correct for 16th knee beams.
1266 = Stem::beam_multiplicity (stem).length () + 1;
1268 Real height_of_my_beams = beam_thickness / 2
1269 + (beam_count - 1) * beam_translation;
1270 Real beam_y = stem_y - d * height_of_my_beams;
1272 Grob *common_y = rest->common_refpoint (beam, Y_AXIS);
1274 Interval rest_extent = rest->extent (rest, Y_AXIS);
1275 rest_extent.translate (offset + rest->get_parent (Y_AXIS)->relative_coordinate (common_y, Y_AXIS));
1277 Real rest_dim = rest_extent[d];
1278 Real minimum_distance
1279 = staff_space * (robust_scm2double (stem->get_property ("stemlet-length"), 0.0)
1280 + robust_scm2double (rest->get_property ("minimum-distance"), 0.0));
1282 Real shift = d * min (d * (beam_y - d * minimum_distance - rest_dim), 0.0);
1284 shift /= staff_space;
1285 Real rad = Staff_symbol_referencer::line_count (rest) * staff_space / 2;
1287 /* Always move discretely by half spaces */
1288 shift = ceil (fabs (shift * 2.0)) / 2.0 * sign (shift);
1290 /* Inside staff, move by whole spaces*/
1291 if ((rest_extent[d] + staff_space * shift) * d
1293 || (rest_extent[-d] + staff_space * shift) * -d
1295 shift = ceil (fabs (shift)) * sign (shift);
1297 return scm_from_double (offset + staff_space * shift);
1300 MAKE_SCHEME_CALLBACK_WITH_OPTARGS (Beam, pure_rest_collision_callback, 4, 1, "");
1302 Beam::pure_rest_collision_callback (SCM smob,
1303 SCM, /* prev_offset */
1309 Grob *me = unsmob_grob (smob);
1310 Grob *stem = unsmob_grob (me->get_object ("stem"));
1312 return scm_from_double (amount);
1313 Grob *beam = unsmob_grob (stem->get_object ("beam"));
1315 || !Beam::normal_stem_count (beam))
1316 return scm_from_double (amount);
1318 Real ss = Staff_symbol_referencer::staff_space (me);
1321 This gives the extrema of rest positions.
1322 In general, beams are never typeset more than one staff space away
1323 from the staff in either direction.
1325 Grob *staff = Staff_symbol_referencer::get_staff_symbol (me);
1326 Interval rest_max_pos = staff ? Staff_symbol::line_span (staff) : Interval (0.0, 0.0);
1327 rest_max_pos.widen (1);
1328 rest_max_pos *= ss / 2;
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 (amount);
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];
1359 Direction beamdir = get_grob_direction (beam);
1361 Take the position between the two bounding head_positions,
1362 then bound it by the minimum and maximum positions outside the staff.
1363 4.0 = 2.0 to get out of staff space * 2.0 for the average
1365 amount = min (max ((Stem::head_positions (left)[beamdir] + Stem::head_positions (right)[beamdir]) / 4.0, rest_max_pos[DOWN]), rest_max_pos[UP]);
1367 return scm_from_double (amount);
1372 Beam::is_knee (Grob *me)
1374 SCM k = me->get_property ("knee");
1375 if (scm_is_bool (k))
1376 return ly_scm2bool (k);
1380 extract_grob_set (me, "stems", stems);
1381 for (vsize i = stems.size (); i--;)
1383 Direction dir = get_grob_direction (stems[i]);
1392 me->set_property ("knee", ly_bool2scm (knee));
1398 Beam::is_cross_staff (Grob *me)
1400 extract_grob_set (me, "stems", stems);
1401 Grob *staff_symbol = Staff_symbol_referencer::get_staff_symbol (me);
1402 for (vsize i = 0; i < stems.size (); i++)
1403 if (Staff_symbol_referencer::get_staff_symbol (stems[i]) != staff_symbol)
1408 MAKE_SCHEME_CALLBACK (Beam, calc_cross_staff, 1)
1410 Beam::calc_cross_staff (SCM smob)
1412 return scm_from_bool (is_cross_staff (unsmob_grob (smob)));
1416 Beam::get_direction_beam_count (Grob *me, Direction d)
1418 extract_grob_set (me, "stems", stems);
1421 for (vsize i = stems.size (); i--;)
1424 Should we take invisible stems into account?
1426 if (get_grob_direction (stems[i]) == d)
1427 bc = max (bc, (Stem::beam_multiplicity (stems[i]).length () + 1));
1433 ADD_INTERFACE (Beam,
1436 "The @code{beam-thickness} property is the weight of beams,"
1437 " measured in staffspace. The @code{direction} property is"
1438 " not user-serviceable. Use the @code{direction} property"
1439 " of @code{Stem} instead.\n"
1440 "The following properties may be set in the @code{details}"
1444 "@item stem-length-demerit-factor\n"
1445 "Demerit factor used for inappropriate stem lengths.\n"
1446 "@item secondary-beam-demerit\n"
1447 "Demerit used in quanting calculations for multiple"
1449 "@item region-size\n"
1450 "Size of region for checking quant scores.\n"
1452 "Epsilon for beam quant code to check for presence"
1454 "@item stem-length-limit-penalty\n"
1455 "Penalty for differences in stem lengths on a beam.\n"
1456 "@item damping-direction-penalty\n"
1457 "Demerit penalty applied when beam direction is different"
1458 " from damping direction.\n"
1459 "@item hint-direction-penalty\n"
1460 "Demerit penalty applied when beam direction is different"
1461 " from damping direction, but damping slope is"
1462 " <= @code{round-to-zero-slope}.\n"
1463 "@item musical-direction-factor\n"
1464 "Demerit scaling factor for difference between"
1465 " beam slope and music slope.\n"
1466 "@item ideal-slope-factor\n"
1467 "Demerit scaling factor for difference between"
1468 " beam slope and damping slope.\n"
1469 "@item round-to-zero-slope\n"
1470 "Damping slope which is considered zero for purposes of"
1471 " calculating direction penalties.\n"
1477 "beamed-stem-shorten "
1484 "collision-interfaces "
1485 "collision-voice-only "
1497 "neutral-direction "
1500 "quantized-positions "