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 // TODO - should store result in a property?
341 Beam::get_beam_segments (Grob *me_grob, Grob **common)
343 /* ugh, this has a side-effect that we need to ensure that
344 Stem #'beaming is correct */
345 (void) me_grob->get_property ("beaming");
347 Spanner *me = dynamic_cast<Spanner *> (me_grob);
349 extract_grob_set (me, "stems", stems);
350 Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
352 commonx = me->get_bound (LEFT)->common_refpoint (commonx, X_AXIS);
353 commonx = me->get_bound (RIGHT)->common_refpoint (commonx, X_AXIS);
357 int gap_count = robust_scm2int (me->get_property ("gap-count"), 0);
358 Real gap_length = robust_scm2double (me->get_property ("gap"), 0.0);
360 Position_stem_segments_map stem_segments;
361 Real lt = me->layout ()->get_dimension (ly_symbol2scm ("line-thickness"));
363 /* There are two concepts of "rank" that are used in the following code.
364 The beam_rank is the vertical position of the beam (larger numbers are
365 closer to the noteheads). Beam_stem_segment.rank_, on the other hand,
366 is the horizontal position of the segment (this is incremented by two
367 for each stem; the beam segment on the right side of the stem has
368 a higher rank (by one) than its neighbour to the left). */
370 for (vsize i = 0; i < stems.size (); i++)
372 Grob *stem = stems[i];
373 Real stem_width = robust_scm2double (stem->get_property ("thickness"), 1.0) * lt;
374 Real stem_x = stem->relative_coordinate (commonx, X_AXIS);
375 SCM beaming = stem->get_property ("beaming");
379 // Find the maximum and minimum beam ranks.
380 // Given that RANKS is never reset to empty, the interval will always be
381 // smallest for the left beamlet of the first stem, and then it might grow.
382 // Do we really want this? (It only affects the tremolo gaps) --jneem
383 for (SCM s = index_get_cell (beaming, d);
384 scm_is_pair (s); s = scm_cdr (s))
386 if (!scm_is_integer (scm_car (s)))
389 int beam_rank = scm_to_int (scm_car (s));
390 ranks.add_point (beam_rank);
393 for (SCM s = index_get_cell (beaming, d);
394 scm_is_pair (s); s = scm_cdr (s))
396 if (!scm_is_integer (scm_car (s)))
399 int beam_rank = scm_to_int (scm_car (s));
400 Beam_stem_segment seg;
402 seg.stem_x_ = stem_x;
403 seg.rank_ = 2 * i + (d + 1) / 2;
404 seg.width_ = stem_width;
407 seg.max_connect_ = robust_scm2int (stem->get_property ("max-beam-connect"), 1000);
409 Direction stem_dir = get_grob_direction (stem);
412 = (stem_dir * beam_rank < (stem_dir * ranks[-stem_dir] + gap_count));
413 stem_segments[beam_rank].push_back (seg);
416 while (flip (&d) != LEFT);
419 Drul_array<Real> break_overshoot
420 = robust_scm2drul (me->get_property ("break-overshoot"),
421 Drul_array<Real> (-0.5, 0.0));
423 vector<Beam_segment> segments;
424 for (Position_stem_segments_map::const_iterator i (stem_segments.begin ());
425 i != stem_segments.end (); i++)
427 vector<Beam_stem_segment> segs = (*i).second;
428 vector_sort (segs, less<Beam_stem_segment> ());
430 Beam_segment current;
432 // Iterate over all of the segments of the current beam rank,
433 // merging the adjacent Beam_stem_segments into one Beam_segment
435 int vertical_count = (*i).first;
436 for (vsize j = 0; j < segs.size (); j++)
438 // Keeping track of the different directions here is a little tricky.
439 // segs[j].dir_ is the direction of the beam segment relative to the stem
440 // (ie. segs[j].dir_ == LEFT if the beam segment sticks out to the left of
441 // its stem) whereas event_dir refers to the edge of the beam segment that
442 // we are currently looking at (ie. if segs[j].dir_ == event_dir then we
443 // are looking at that edge of the beam segment that is furthest from its
445 Direction event_dir = LEFT;
446 Beam_stem_segment const &seg = segs[j];
449 Beam_stem_segment const &neighbor_seg = segs[j + event_dir];
450 // TODO: make names clearer? --jneem
451 // on_line_bound: whether the current segment is on the boundary of the WHOLE beam
452 // on_beam_bound: whether the current segment is on the boundary of just that part
453 // of the beam with the current beam_rank
454 bool on_line_bound = (seg.dir_ == LEFT) ? seg.stem_index_ == 0
455 : seg.stem_index_ == stems.size () - 1;
456 bool on_beam_bound = (event_dir == LEFT) ? j == 0
457 : j == segs.size () - 1;
458 bool inside_stem = (event_dir == LEFT)
459 ? seg.stem_index_ > 0
460 : seg.stem_index_ + 1 < stems.size ();
462 bool event = on_beam_bound
463 || abs (seg.rank_ - neighbor_seg.rank_) > 1
464 || (abs (vertical_count) >= seg.max_connect_
465 || abs (vertical_count) >= neighbor_seg.max_connect_);
468 // Then this edge of the current segment is irrelevent because it will
469 // be connected with the next segment in the event_dir direction.
472 current.vertical_count_ = vertical_count;
473 current.horizontal_[event_dir] = seg.stem_x_;
474 if (seg.dir_ == event_dir)
475 // then we are examining the edge of a beam segment that is furthest
479 && me->get_bound (event_dir)->break_status_dir ())
481 current.horizontal_[event_dir]
482 = (robust_relative_extent (me->get_bound (event_dir),
483 commonx, X_AXIS)[RIGHT]
484 + event_dir * break_overshoot[event_dir]);
488 Grob *stem = stems[seg.stem_index_];
489 Drul_array<Real> beamlet_length
490 = robust_scm2interval (stem->get_property ("beamlet-default-length"), Interval (1.1, 1.1));
491 Drul_array<Real> max_proportion
492 = robust_scm2interval (stem->get_property ("beamlet-max-length-proportion"), Interval (0.75, 0.75));
493 Real length = beamlet_length[seg.dir_];
497 Grob *neighbor_stem = stems[seg.stem_index_ + event_dir];
498 Real neighbor_stem_x = neighbor_stem->relative_coordinate (commonx, X_AXIS);
500 length = min (length,
501 fabs (neighbor_stem_x - seg.stem_x_) * max_proportion[seg.dir_]);
503 current.horizontal_[event_dir] += event_dir * length;
507 // we are examining the edge of a beam segment that is closest
508 // (ie. touching, unless there is a gap) its stem.
510 current.horizontal_[event_dir] += event_dir * seg.width_ / 2;
513 current.horizontal_[event_dir] -= event_dir * gap_length;
515 if (Stem::is_invisible (seg.stem_))
518 Need to do this in case of whole notes. We don't want the
519 heads to collide with the beams.
521 extract_grob_set (seg.stem_, "note-heads", heads);
523 for (vsize k = 0; k < heads.size (); k++)
524 current.horizontal_[event_dir]
525 = event_dir * min (event_dir * current.horizontal_[event_dir],
528 * heads[k]->extent (commonx,
529 X_AXIS)[-event_dir]);
534 if (event_dir == RIGHT)
536 segments.push_back (current);
537 current = Beam_segment ();
540 while (flip (&event_dir) != LEFT);
548 MAKE_SCHEME_CALLBACK (Beam, print, 1);
550 Beam::print (SCM grob)
552 Spanner *me = unsmob_spanner (grob);
554 vector<Beam_segment> segments = get_beam_segments (me, &commonx);
555 if (!segments.size ())
559 if (normal_stem_count (me))
561 span[LEFT] = first_normal_stem (me)->relative_coordinate (commonx, X_AXIS);
562 span[RIGHT] = last_normal_stem (me)->relative_coordinate (commonx, X_AXIS);
566 extract_grob_set (me, "stems", stems);
567 span[LEFT] = stems[0]->relative_coordinate (commonx, X_AXIS);
568 span[RIGHT] = stems.back ()->relative_coordinate (commonx, X_AXIS);
571 Real blot = me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
573 SCM posns = me->get_property ("quantized-positions");
575 if (!is_number_pair (posns))
577 programming_error ("no beam positions?");
578 pos = Interval (0, 0);
581 pos = ly_scm2realdrul (posns);
583 scale_drul (&pos, Staff_symbol_referencer::staff_space (me));
585 Real dy = pos[RIGHT] - pos[LEFT];
586 Real slope = (dy && span.length ()) ? dy / span.length () : 0;
588 Real beam_thickness = get_beam_thickness (me);
589 Real beam_dy = get_beam_translation (me);
591 Direction feather_dir = to_dir (me->get_property ("grow-direction"));
593 Interval placements = robust_scm2interval (me->get_property ("normalized-endpoints"), Interval (0.0, 0.0));
597 int extreme = (segments[0].vertical_count_ == 0
598 ? segments[0].vertical_count_
599 : segments.back ().vertical_count_);
601 for (vsize i = 0; i < segments.size (); i++)
603 Real local_slope = slope;
605 Makes local slope proportional to the ratio of the length of this beam
609 local_slope += (feather_dir * segments[i].vertical_count_
611 * placements.length ()
614 Stencil b = Lookup::beam (local_slope, segments[i].horizontal_.length (), beam_thickness, blot);
616 b.translate_axis (segments[i].horizontal_[LEFT], X_AXIS);
617 Real multiplier = feather_dir ? placements[LEFT] : 1.0;
619 Interval weights (1 - multiplier, multiplier);
621 if (feather_dir != LEFT)
624 // we need two translations: the normal one and
625 // the one of the lowest segment
626 int idx[] = {i, extreme};
627 Real translations[2];
629 for (int j = 0; j < 2; j++)
630 translations[j] = slope
631 * (segments[idx[j]].horizontal_[LEFT] - span.linear_combination (CENTER))
632 + pos.linear_combination (CENTER)
633 + beam_dy * segments[idx[j]].vertical_count_;
635 Real weighted_average = translations[0] * weights[LEFT] + translations[1] * weights[RIGHT];
638 Tricky. The manipulation of the variable `weighted_average' below ensures
639 that beams with a RIGHT grow direction will start from the position of the
640 lowest segment at 0, and this error will decrease and decrease over the
641 course of the beam. Something with a LEFT grow direction, on the other
642 hand, will always start in the correct place but progressively accrue
643 error at broken places. This code shifts beams up given where they are
644 in the total span length (controlled by the variable `multiplier'). To
645 better understand what it does, try commenting it out: you'll see that
646 all of the RIGHT growing beams immediately start too low and get better
647 over line breaks, whereas all of the LEFT growing beams start just right
648 and get worse over line breaks.
650 Real factor = Interval (multiplier, 1 - multiplier).linear_combination (feather_dir);
652 if (segments[0].vertical_count_ < 0 && feather_dir)
653 weighted_average += beam_dy * (segments.size () - 1) * factor;
655 b.translate_axis (weighted_average, Y_AXIS);
657 the_beam.add_stencil (b);
661 #if (DEBUG_BEAM_SCORING)
662 SCM annotation = me->get_property ("annotation");
663 if (scm_is_string (annotation))
665 extract_grob_set (me, "stems", stems);
668 This code prints the demerits for each beam. Perhaps this
669 should be switchable for those who want to twiddle with the
673 SCM properties = Font_interface::text_font_alist_chain (me);
675 properties = scm_cons (scm_acons (ly_symbol2scm ("font-size"), scm_from_int (-5), SCM_EOL),
678 Direction stem_dir = stems.size () ? to_dir (stems[0]->get_property ("direction")) : UP;
680 Stencil score = *unsmob_stencil (Text_interface::interpret_markup
681 (me->layout ()->self_scm (), properties, annotation));
683 if (!score.is_empty ())
685 score.translate_axis (me->relative_coordinate (commonx, X_AXIS), X_AXIS);
686 the_beam.add_at_edge (Y_AXIS, stem_dir, score, 1.0);
691 the_beam.translate_axis (-me->relative_coordinate (commonx, X_AXIS), X_AXIS);
692 return the_beam.smobbed_copy ();
696 Beam::get_default_dir (Grob *me)
698 extract_grob_set (me, "stems", stems);
700 Drul_array<Real> extremes (0.0, 0.0);
701 for (iterof (s, stems); s != stems.end (); s++)
703 Interval positions = Stem::head_positions (*s);
707 if (sign (positions[d]) == d)
708 extremes[d] = d * max (d * positions[d], d * extremes[d]);
710 while (flip (&d) != DOWN);
713 Drul_array<int> total (0, 0);
714 Drul_array<int> count (0, 0);
716 bool force_dir = false;
717 for (vsize i = 0; i < stems.size (); i++)
720 Direction stem_dir = CENTER;
721 SCM stem_dir_scm = s->get_property_data ("direction");
722 if (is_direction (stem_dir_scm))
724 stem_dir = to_dir (stem_dir_scm);
728 stem_dir = to_dir (s->get_property ("default-direction"));
731 stem_dir = to_dir (s->get_property ("neutral-direction"));
736 total[stem_dir] += max (int (- stem_dir * Stem::head_positions (s) [-stem_dir]), 0);
742 if (abs (extremes[UP]) > -extremes[DOWN])
744 else if (extremes[UP] < -extremes[DOWN])
748 Direction dir = CENTER;
749 Direction d = CENTER;
750 if ((d = (Direction) sign (count[UP] - count[DOWN])))
754 && (d = (Direction) sign (total[UP] / count[UP] - total[DOWN] / count[DOWN])))
756 else if ((d = (Direction) sign (total[UP] - total[DOWN])))
759 dir = to_dir (me->get_property ("neutral-direction"));
764 /* Set all stems with non-forced direction to beam direction.
765 Urg: non-forced should become `without/with unforced' direction,
766 once stem gets cleaned-up. */
768 Beam::set_stem_directions (Grob *me, Direction d)
770 extract_grob_set (me, "stems", stems);
772 for (vsize i = 0; i < stems.size (); i++)
776 SCM forcedir = s->get_property_data ("direction");
777 if (!to_dir (forcedir))
778 set_grob_direction (s, d);
783 Only try horizontal beams for knees. No reliable detection of
784 anything else is possible here, since we don't know funky-beaming
785 settings, or X-distances (slopes!) People that want sloped
786 knee-beams, should set the directions manually.
791 this routine should take into account the stemlength scoring
792 of a possible knee/nonknee beam.
795 Beam::consider_auto_knees (Grob *me)
797 SCM scm = me->get_property ("auto-knee-gap");
798 if (!scm_is_number (scm))
805 extract_grob_set (me, "normal-stems", stems);
807 Grob *common = common_refpoint_of_array (stems, me, Y_AXIS);
808 Real staff_space = Staff_symbol_referencer::staff_space (me);
810 vector<Interval> head_extents_array;
811 for (vsize i = 0; i < stems.size (); i++)
813 Grob *stem = stems[i];
815 Interval head_extents = Stem::head_positions (stem);
816 if (!head_extents.is_empty ())
818 head_extents[LEFT] += -1;
819 head_extents[RIGHT] += 1;
820 head_extents *= staff_space * 0.5;
823 We could subtract beam Y position, but this routine only
824 sets stem directions, a constant shift does not have an
827 head_extents += stem->pure_relative_y_coordinate (common, 0, INT_MAX);
829 if (to_dir (stem->get_property_data ("direction")))
831 Direction stemdir = to_dir (stem->get_property ("direction"));
832 head_extents[-stemdir] = -stemdir * infinity_f;
835 head_extents_array.push_back (head_extents);
837 gaps.remove_interval (head_extents);
841 Real max_gap_len = 0.0;
843 for (vsize i = gaps.allowed_regions_.size () - 1; i != VPOS; i--)
845 Interval gap = gaps.allowed_regions_[i];
848 the outer gaps are not knees.
850 if (isinf (gap[LEFT]) || isinf (gap[RIGHT]))
853 if (gap.length () >= max_gap_len)
855 max_gap_len = gap.length ();
860 Real beam_translation = get_beam_translation (me);
861 Real beam_thickness = Beam::get_beam_thickness (me);
862 int beam_count = Beam::get_beam_count (me);
863 Real height_of_beams = beam_thickness / 2
864 + (beam_count - 1) * beam_translation;
865 Real threshold = scm_to_double (scm) + height_of_beams;
867 if (max_gap_len > threshold)
870 for (vsize i = 0; i < stems.size (); i++)
872 Grob *stem = stems[i];
873 Interval head_extents = head_extents_array[j++];
875 Direction d = (head_extents.center () < max_gap.center ())
878 stem->set_property ("direction", scm_from_int (d));
880 head_extents.intersect (max_gap);
881 assert (head_extents.is_empty () || head_extents.length () < 1e-6);
886 MAKE_SCHEME_CALLBACK (Beam, calc_stem_shorten, 1)
888 Beam::calc_stem_shorten (SCM smob)
890 Grob *me = unsmob_grob (smob);
893 shortening looks silly for x staff beams
896 return scm_from_int (0);
898 Real forced_fraction = 1.0 * forced_stem_count (me)
899 / normal_stem_count (me);
901 int beam_count = get_beam_count (me);
903 SCM shorten_list = me->get_property ("beamed-stem-shorten");
904 if (shorten_list == SCM_EOL)
905 return scm_from_int (0);
907 Real staff_space = Staff_symbol_referencer::staff_space (me);
910 = robust_list_ref (beam_count - 1, shorten_list);
911 Real shorten = scm_to_double (shorten_elt) * staff_space;
913 shorten *= forced_fraction;
916 return scm_from_double (shorten);
918 return scm_from_double (0.0);
921 MAKE_SCHEME_CALLBACK (Beam, quanting, 2);
923 Beam::quanting (SCM smob, SCM posns)
925 Grob *me = unsmob_grob (smob);
926 Drul_array<Real> ys (0, 0);
927 ys = robust_scm2drul (posns, ys);
928 Beam_scoring_problem problem (me, ys);
930 ys = problem.solve ();
931 return ly_interval2scm (ys);
935 Report slice containing the numbers that are both in (car BEAMING)
939 where_are_the_whole_beams (SCM beaming)
943 for (SCM s = scm_car (beaming); scm_is_pair (s); s = scm_cdr (s))
945 if (scm_c_memq (scm_car (s), scm_cdr (beaming)) != SCM_BOOL_F)
947 l.add_point (scm_to_int (scm_car (s)));
953 /* Return the Y position of the stem-end, given the Y-left, Y-right
954 in POS for stem S. This Y position is relative to S. */
956 Beam::calc_stem_y (Grob *me, Grob *stem, Grob **common,
957 Real xl, Real xr, Direction feather_dir,
958 Drul_array<Real> pos, bool french)
960 Real beam_translation = get_beam_translation (me);
961 Direction stem_dir = get_grob_direction (stem);
964 Real relx = dx ? (stem->relative_coordinate (common[X_AXIS], X_AXIS) - xl) / dx : 0;
965 Real xdir = 2 * relx - 1;
967 Real stem_y = linear_combination (pos, xdir);
969 SCM beaming = stem->get_property ("beaming");
971 Slice beam_slice (french
972 ? where_are_the_whole_beams (beaming)
973 : Stem::beam_multiplicity (stem));
974 if (beam_slice.is_empty ())
975 beam_slice = Slice (0, 0);
976 Interval beam_multiplicity (beam_slice[LEFT],
980 feather dir = 1 , relx 0->1 : factor 0 -> 1
981 feather dir = 0 , relx 0->1 : factor 1 -> 1
982 feather dir = -1, relx 0->1 : factor 1 -> 0
984 Real feather_factor = 1;
986 feather_factor = relx;
987 else if (feather_dir < 0)
988 feather_factor = 1 - relx;
990 stem_y += feather_factor * beam_translation
991 * beam_multiplicity[Direction (((french) ? DOWN : UP) * stem_dir)];
992 Real id = me->relative_coordinate (common[Y_AXIS], Y_AXIS)
993 - stem->relative_coordinate (common[Y_AXIS], Y_AXIS);
999 Hmm. At this time, beam position and slope are determined. Maybe,
1000 stem directions and length should set to relative to the chord's
1001 position of the beam. */
1002 MAKE_SCHEME_CALLBACK (Beam, set_stem_lengths, 1);
1004 Beam::set_stem_lengths (SCM smob)
1006 Grob *me = unsmob_grob (smob);
1008 /* trigger callbacks. */
1009 (void) me->get_property ("direction");
1010 (void) me->get_property ("beaming");
1012 SCM posns = me->get_property ("positions");
1014 extract_grob_set (me, "stems", stems);
1019 for (int a = 2; a--;)
1020 common[a] = common_refpoint_of_array (stems, me, Axis (a));
1022 Drul_array<Real> pos = ly_scm2realdrul (posns);
1023 Real staff_space = Staff_symbol_referencer::staff_space (me);
1024 scale_drul (&pos, staff_space);
1028 if (robust_scm2int (me->get_property ("gap-count"), 0))
1031 thick = get_beam_thickness (me);
1034 Grob *fvs = first_normal_stem (me);
1035 Grob *lvs = last_normal_stem (me);
1037 Real xl = fvs ? fvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
1038 Real xr = lvs ? lvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
1039 Direction feather_dir = to_dir (me->get_property ("grow-direction"));
1041 for (vsize i = 0; i < stems.size (); i++)
1045 bool french = to_boolean (s->get_property ("french-beaming"));
1046 Real stem_y = calc_stem_y (me, s, common,
1047 xl, xr, feather_dir,
1048 pos, french && s != lvs && s != fvs);
1051 Make the stems go up to the end of the beam. This doesn't matter
1052 for normal beams, but for tremolo beams it looks silly otherwise.
1055 && !Stem::is_invisible (s))
1056 stem_y += thick * 0.5 * get_grob_direction (s);
1059 Do set_stem_positions for invisible stems too, so tuplet brackets
1060 have a reference point for sloping
1062 Stem::set_stem_positions (s, 2 * stem_y / staff_space);
1069 Beam::set_beaming (Grob *me, Beaming_pattern const *beaming)
1071 extract_grob_set (me, "stems", stems);
1074 for (vsize i = 0; i < stems.size (); i++)
1077 Don't overwrite user settings.
1081 Grob *stem = stems[i];
1082 SCM beaming_prop = stem->get_property ("beaming");
1083 if (beaming_prop == SCM_EOL
1084 || index_get_cell (beaming_prop, d) == SCM_EOL)
1086 int count = beaming->beamlet_count (i, d);
1088 && i + 1 < stems.size ()
1089 && Stem::is_invisible (stem))
1090 count = min (count, beaming->beamlet_count (i, -d));
1092 if ( ((i == 0 && d == LEFT)
1093 || (i == stems.size () - 1 && d == RIGHT))
1094 && stems.size () > 1
1095 && to_boolean (me->get_property ("clip-edges")))
1098 Stem::set_beaming (stem, count, d);
1101 while (flip (&d) != LEFT);
1106 Beam::forced_stem_count (Grob *me)
1108 extract_grob_set (me, "normal-stems", stems);
1111 for (vsize i = 0; i < stems.size (); i++)
1115 /* I can imagine counting those boundaries as a half forced stem,
1116 but let's count them full for now. */
1117 Direction defdir = to_dir (s->get_property ("default-direction"));
1119 if (abs (Stem::chord_start_y (s)) > 0.1
1121 && get_grob_direction (s) != defdir)
1128 Beam::normal_stem_count (Grob *me)
1130 extract_grob_set (me, "normal-stems", stems);
1131 return stems.size ();
1135 Beam::first_normal_stem (Grob *me)
1137 extract_grob_set (me, "normal-stems", stems);
1138 return stems.size () ? stems[0] : 0;
1142 Beam::last_normal_stem (Grob *me)
1144 extract_grob_set (me, "normal-stems", stems);
1145 return stems.size () ? stems.back () : 0;
1151 handle rest under beam (do_post: beams are calculated now)
1152 what about combination of collisions and rest under beam.
1156 rest -> stem -> beam -> interpolate_y_position ()
1158 MAKE_SCHEME_CALLBACK_WITH_OPTARGS (Beam, rest_collision_callback, 2, 1, "");
1160 Beam::rest_collision_callback (SCM smob, SCM prev_offset)
1162 Grob *rest = unsmob_grob (smob);
1163 if (scm_is_number (rest->get_property ("staff-position")))
1164 return scm_from_int (0);
1166 Real offset = robust_scm2double (prev_offset, 0.0);
1168 Grob *st = unsmob_grob (rest->get_object ("stem"));
1171 return scm_from_double (0.0);
1172 Grob *beam = unsmob_grob (stem->get_object ("beam"));
1174 || !Beam::has_interface (beam)
1175 || !Beam::normal_stem_count (beam))
1176 return scm_from_double (0.0);
1178 Drul_array<Real> pos (robust_scm2drul (beam->get_property ("positions"),
1179 Drul_array<Real> (0, 0)));
1181 Real staff_space = Staff_symbol_referencer::staff_space (rest);
1183 scale_drul (&pos, staff_space);
1185 Real dy = pos[RIGHT] - pos[LEFT];
1187 Drul_array<Grob *> visible_stems (first_normal_stem (beam),
1188 last_normal_stem (beam));
1189 extract_grob_set (beam, "stems", stems);
1191 Grob *common = common_refpoint_of_array (stems, beam, X_AXIS);
1193 Real x0 = visible_stems[LEFT]->relative_coordinate (common, X_AXIS);
1194 Real dx = visible_stems[RIGHT]->relative_coordinate (common, X_AXIS) - x0;
1195 Real slope = dy && dx ? dy / dx : 0;
1197 Direction d = get_grob_direction (stem);
1198 Real stem_y = pos[LEFT]
1199 + (stem->relative_coordinate (common, X_AXIS) - x0) * slope;
1201 Real beam_translation = get_beam_translation (beam);
1202 Real beam_thickness = Beam::get_beam_thickness (beam);
1205 TODO: this is not strictly correct for 16th knee beams.
1208 = Stem::beam_multiplicity (stem).length () + 1;
1210 Real height_of_my_beams = beam_thickness / 2
1211 + (beam_count - 1) * beam_translation;
1212 Real beam_y = stem_y - d * height_of_my_beams;
1214 Grob *common_y = rest->common_refpoint (beam, Y_AXIS);
1216 Interval rest_extent = rest->extent (rest, Y_AXIS);
1217 rest_extent.translate (offset + rest->get_parent (Y_AXIS)->relative_coordinate (common_y, Y_AXIS));
1219 Real rest_dim = rest_extent[d];
1220 Real minimum_distance
1221 = staff_space * (robust_scm2double (stem->get_property ("stemlet-length"), 0.0)
1222 + robust_scm2double (rest->get_property ("minimum-distance"), 0.0));
1224 Real shift = d * min (d * (beam_y - d * minimum_distance - rest_dim), 0.0);
1226 shift /= staff_space;
1227 Real rad = Staff_symbol_referencer::line_count (rest) * staff_space / 2;
1229 /* Always move discretely by half spaces */
1230 shift = ceil (fabs (shift * 2.0)) / 2.0 * sign (shift);
1232 /* Inside staff, move by whole spaces*/
1233 if ((rest_extent[d] + staff_space * shift) * d
1235 || (rest_extent[-d] + staff_space * shift) * -d
1237 shift = ceil (fabs (shift)) * sign (shift);
1239 return scm_from_double (offset + staff_space * shift);
1242 MAKE_SCHEME_CALLBACK_WITH_OPTARGS (Beam, pure_rest_collision_callback, 4, 1, "");
1244 Beam::pure_rest_collision_callback (SCM smob,
1245 SCM, /* prev_offset */
1251 Grob *me = unsmob_grob (smob);
1252 Grob *stem = unsmob_grob (me->get_object ("stem"));
1254 return scm_from_double (amount);
1255 Grob *beam = unsmob_grob (stem->get_object ("beam"));
1257 || !Beam::normal_stem_count (beam))
1258 return scm_from_double (amount);
1260 Real ss = Staff_symbol_referencer::staff_space (me);
1263 This gives the extrema of rest positions.
1264 In general, beams are never typeset more than one staff space away
1265 from the staff in either direction.
1267 Grob *staff = Staff_symbol_referencer::get_staff_symbol (me);
1268 Interval rest_max_pos = staff ? Staff_symbol::line_span (staff) : Interval (0.0, 0.0);
1269 rest_max_pos.widen (1);
1270 rest_max_pos *= ss / 2;
1272 extract_grob_set (beam, "stems", stems);
1273 vector<Grob *> my_stems;
1275 for (vsize i = 0; i < stems.size (); i++)
1276 if (Stem::head_count (stems[i]) || stems[i] == stem)
1277 my_stems.push_back (stems[i]);
1281 for (vsize i = 0; i < my_stems.size (); i++)
1282 if (my_stems[i] == stem)
1290 if (idx == (vsize)-1 || my_stems.size () == 1)
1291 return scm_from_double (amount);
1293 left = right = my_stems[1];
1294 else if (idx == my_stems.size () - 1)
1295 left = right = my_stems[idx - 1];
1298 left = my_stems[idx - 1];
1299 right = my_stems[idx + 1];
1301 Direction beamdir = get_grob_direction (beam);
1303 Take the position between the two bounding head_positions,
1304 then bound it by the minimum and maximum positions outside the staff.
1305 4.0 = 2.0 to get out of staff space * 2.0 for the average
1307 amount = min (max ((Stem::head_positions (left)[beamdir] + Stem::head_positions (right)[beamdir]) / 4.0, rest_max_pos[DOWN]), rest_max_pos[UP]);
1309 return scm_from_double (amount);
1314 Beam::is_knee (Grob *me)
1316 SCM k = me->get_property ("knee");
1317 if (scm_is_bool (k))
1318 return ly_scm2bool (k);
1322 extract_grob_set (me, "stems", stems);
1323 for (vsize i = stems.size (); i--;)
1325 Direction dir = get_grob_direction (stems[i]);
1334 me->set_property ("knee", ly_bool2scm (knee));
1340 Beam::is_cross_staff (Grob *me)
1342 extract_grob_set (me, "stems", stems);
1343 Grob *staff_symbol = Staff_symbol_referencer::get_staff_symbol (me);
1344 for (vsize i = 0; i < stems.size (); i++)
1345 if (Staff_symbol_referencer::get_staff_symbol (stems[i]) != staff_symbol)
1350 MAKE_SCHEME_CALLBACK (Beam, calc_cross_staff, 1)
1352 Beam::calc_cross_staff (SCM smob)
1354 return scm_from_bool (is_cross_staff (unsmob_grob (smob)));
1358 Beam::get_direction_beam_count (Grob *me, Direction d)
1360 extract_grob_set (me, "stems", stems);
1363 for (vsize i = stems.size (); i--;)
1366 Should we take invisible stems into account?
1368 if (get_grob_direction (stems[i]) == d)
1369 bc = max (bc, (Stem::beam_multiplicity (stems[i]).length () + 1));
1375 ADD_INTERFACE (Beam,
1378 "The @code{beam-thickness} property is the weight of beams,"
1379 " measured in staffspace. The @code{direction} property is"
1380 " not user-serviceable. Use the @code{direction} property"
1381 " of @code{Stem} instead.\n"
1383 "The following properties may be set in the @code{details}"
1387 "@item stem-length-demerit-factor\n"
1388 "Demerit factor used for inappropriate stem lengths.\n"
1389 "@item secondary-beam-demerit\n"
1390 "Demerit used in quanting calculations for multiple"
1392 "@item region-size\n"
1393 "Size of region for checking quant scores.\n"
1395 "Epsilon for beam quant code to check for presence"
1397 "@item stem-length-limit-penalty\n"
1398 "Penalty for differences in stem lengths on a beam.\n"
1399 "@item damping-direction-penalty\n"
1400 "Demerit penalty applied when beam direction is different"
1401 " from damping direction.\n"
1402 "@item hint-direction-penalty\n"
1403 "Demerit penalty applied when beam direction is different"
1404 " from damping direction, but damping slope is"
1405 " <= @code{round-to-zero-slope}.\n"
1406 "@item musical-direction-factor\n"
1407 "Demerit scaling factor for difference between"
1408 " beam slope and music slope.\n"
1409 "@item ideal-slope-factor\n"
1410 "Demerit scaling factor for difference between"
1411 " beam slope and damping slope.\n"
1412 "@item round-to-zero-slope\n"
1413 "Damping slope which is considered zero for purposes of"
1414 " calculating direction penalties.\n"
1420 "beamed-stem-shorten "
1426 "collision-interfaces "
1427 "collision-voice-only "
1439 "neutral-direction "
1442 "quantized-positions "