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, 1);
923 Beam::quanting (SCM smob)
925 Grob *me = unsmob_grob (smob);
926 Drul_array<Real> ys (0, 0);
927 Beam_scoring_problem problem (me, ys);
929 ys = problem.solve ();
930 return ly_interval2scm (ys);
934 Report slice containing the numbers that are both in (car BEAMING)
938 where_are_the_whole_beams (SCM beaming)
942 for (SCM s = scm_car (beaming); scm_is_pair (s); s = scm_cdr (s))
944 if (scm_c_memq (scm_car (s), scm_cdr (beaming)) != SCM_BOOL_F)
946 l.add_point (scm_to_int (scm_car (s)));
952 /* Return the Y position of the stem-end, given the Y-left, Y-right
953 in POS for stem S. This Y position is relative to S. */
955 Beam::calc_stem_y (Grob *me, Grob *stem, Grob **common,
956 Real xl, Real xr, Direction feather_dir,
957 Drul_array<Real> pos, bool french)
959 Real beam_translation = get_beam_translation (me);
960 Direction stem_dir = get_grob_direction (stem);
963 Real relx = dx ? (stem->relative_coordinate (common[X_AXIS], X_AXIS) - xl) / dx : 0;
964 Real xdir = 2 * relx - 1;
966 Real stem_y = linear_combination (pos, xdir);
968 SCM beaming = stem->get_property ("beaming");
970 Slice beam_slice (french
971 ? where_are_the_whole_beams (beaming)
972 : Stem::beam_multiplicity (stem));
973 if (beam_slice.is_empty ())
974 beam_slice = Slice (0, 0);
975 Interval beam_multiplicity (beam_slice[LEFT],
979 feather dir = 1 , relx 0->1 : factor 0 -> 1
980 feather dir = 0 , relx 0->1 : factor 1 -> 1
981 feather dir = -1, relx 0->1 : factor 1 -> 0
983 Real feather_factor = 1;
985 feather_factor = relx;
986 else if (feather_dir < 0)
987 feather_factor = 1 - relx;
989 stem_y += feather_factor * beam_translation
990 * beam_multiplicity[Direction (((french) ? DOWN : UP) * stem_dir)];
991 Real id = me->relative_coordinate (common[Y_AXIS], Y_AXIS)
992 - stem->relative_coordinate (common[Y_AXIS], Y_AXIS);
998 Hmm. At this time, beam position and slope are determined. Maybe,
999 stem directions and length should set to relative to the chord's
1000 position of the beam. */
1001 MAKE_SCHEME_CALLBACK (Beam, set_stem_lengths, 1);
1003 Beam::set_stem_lengths (SCM smob)
1005 Grob *me = unsmob_grob (smob);
1007 /* trigger callbacks. */
1008 (void) me->get_property ("direction");
1009 (void) me->get_property ("beaming");
1011 SCM posns = me->get_property ("positions");
1013 extract_grob_set (me, "stems", stems);
1018 for (int a = 2; a--;)
1019 common[a] = common_refpoint_of_array (stems, me, Axis (a));
1021 Drul_array<Real> pos = ly_scm2realdrul (posns);
1022 Real staff_space = Staff_symbol_referencer::staff_space (me);
1023 scale_drul (&pos, staff_space);
1027 if (robust_scm2int (me->get_property ("gap-count"), 0))
1030 thick = get_beam_thickness (me);
1033 Grob *fvs = first_normal_stem (me);
1034 Grob *lvs = last_normal_stem (me);
1036 Real xl = fvs ? fvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
1037 Real xr = lvs ? lvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
1038 Direction feather_dir = to_dir (me->get_property ("grow-direction"));
1040 for (vsize i = 0; i < stems.size (); i++)
1044 bool french = to_boolean (s->get_property ("french-beaming"));
1045 Real stem_y = calc_stem_y (me, s, common,
1046 xl, xr, feather_dir,
1047 pos, french && s != lvs && s != fvs);
1050 Make the stems go up to the end of the beam. This doesn't matter
1051 for normal beams, but for tremolo beams it looks silly otherwise.
1054 && !Stem::is_invisible (s))
1055 stem_y += thick * 0.5 * get_grob_direction (s);
1058 Do set_stem_positions for invisible stems too, so tuplet brackets
1059 have a reference point for sloping
1061 Stem::set_stem_positions (s, 2 * stem_y / staff_space);
1068 Beam::set_beaming (Grob *me, Beaming_pattern const *beaming)
1070 extract_grob_set (me, "stems", stems);
1073 for (vsize i = 0; i < stems.size (); i++)
1076 Don't overwrite user settings.
1080 Grob *stem = stems[i];
1081 SCM beaming_prop = stem->get_property ("beaming");
1082 if (beaming_prop == SCM_EOL
1083 || index_get_cell (beaming_prop, d) == SCM_EOL)
1085 int count = beaming->beamlet_count (i, d);
1087 && i + 1 < stems.size ()
1088 && Stem::is_invisible (stem))
1089 count = min (count, beaming->beamlet_count (i, -d));
1091 if ( ((i == 0 && d == LEFT)
1092 || (i == stems.size () - 1 && d == RIGHT))
1093 && stems.size () > 1
1094 && to_boolean (me->get_property ("clip-edges")))
1097 Stem::set_beaming (stem, count, d);
1100 while (flip (&d) != LEFT);
1105 Beam::forced_stem_count (Grob *me)
1107 extract_grob_set (me, "normal-stems", stems);
1110 for (vsize i = 0; i < stems.size (); i++)
1114 /* I can imagine counting those boundaries as a half forced stem,
1115 but let's count them full for now. */
1116 Direction defdir = to_dir (s->get_property ("default-direction"));
1118 if (abs (Stem::chord_start_y (s)) > 0.1
1120 && get_grob_direction (s) != defdir)
1127 Beam::normal_stem_count (Grob *me)
1129 extract_grob_set (me, "normal-stems", stems);
1130 return stems.size ();
1134 Beam::first_normal_stem (Grob *me)
1136 extract_grob_set (me, "normal-stems", stems);
1137 return stems.size () ? stems[0] : 0;
1141 Beam::last_normal_stem (Grob *me)
1143 extract_grob_set (me, "normal-stems", stems);
1144 return stems.size () ? stems.back () : 0;
1150 handle rest under beam (do_post: beams are calculated now)
1151 what about combination of collisions and rest under beam.
1155 rest -> stem -> beam -> interpolate_y_position ()
1157 MAKE_SCHEME_CALLBACK_WITH_OPTARGS (Beam, rest_collision_callback, 2, 1, "");
1159 Beam::rest_collision_callback (SCM smob, SCM prev_offset)
1161 Grob *rest = unsmob_grob (smob);
1162 if (scm_is_number (rest->get_property ("staff-position")))
1163 return scm_from_int (0);
1165 Real offset = robust_scm2double (prev_offset, 0.0);
1167 Grob *st = unsmob_grob (rest->get_object ("stem"));
1170 return scm_from_double (0.0);
1171 Grob *beam = unsmob_grob (stem->get_object ("beam"));
1173 || !Beam::has_interface (beam)
1174 || !Beam::normal_stem_count (beam))
1175 return scm_from_double (0.0);
1177 Drul_array<Real> pos (robust_scm2drul (beam->get_property ("positions"),
1178 Drul_array<Real> (0, 0)));
1180 Real staff_space = Staff_symbol_referencer::staff_space (rest);
1182 scale_drul (&pos, staff_space);
1184 Real dy = pos[RIGHT] - pos[LEFT];
1186 Drul_array<Grob *> visible_stems (first_normal_stem (beam),
1187 last_normal_stem (beam));
1188 extract_grob_set (beam, "stems", stems);
1190 Grob *common = common_refpoint_of_array (stems, beam, X_AXIS);
1192 Real x0 = visible_stems[LEFT]->relative_coordinate (common, X_AXIS);
1193 Real dx = visible_stems[RIGHT]->relative_coordinate (common, X_AXIS) - x0;
1194 Real slope = dy && dx ? dy / dx : 0;
1196 Direction d = get_grob_direction (stem);
1197 Real stem_y = pos[LEFT]
1198 + (stem->relative_coordinate (common, X_AXIS) - x0) * slope;
1200 Real beam_translation = get_beam_translation (beam);
1201 Real beam_thickness = Beam::get_beam_thickness (beam);
1204 TODO: this is not strictly correct for 16th knee beams.
1207 = Stem::beam_multiplicity (stem).length () + 1;
1209 Real height_of_my_beams = beam_thickness / 2
1210 + (beam_count - 1) * beam_translation;
1211 Real beam_y = stem_y - d * height_of_my_beams;
1213 Grob *common_y = rest->common_refpoint (beam, Y_AXIS);
1215 Interval rest_extent = rest->extent (rest, Y_AXIS);
1216 rest_extent.translate (offset + rest->get_parent (Y_AXIS)->relative_coordinate (common_y, Y_AXIS));
1218 Real rest_dim = rest_extent[d];
1219 Real minimum_distance
1220 = staff_space * (robust_scm2double (stem->get_property ("stemlet-length"), 0.0)
1221 + robust_scm2double (rest->get_property ("minimum-distance"), 0.0));
1223 Real shift = d * min (d * (beam_y - d * minimum_distance - rest_dim), 0.0);
1225 shift /= staff_space;
1226 Real rad = Staff_symbol_referencer::line_count (rest) * staff_space / 2;
1228 /* Always move discretely by half spaces */
1229 shift = ceil (fabs (shift * 2.0)) / 2.0 * sign (shift);
1231 /* Inside staff, move by whole spaces*/
1232 if ((rest_extent[d] + staff_space * shift) * d
1234 || (rest_extent[-d] + staff_space * shift) * -d
1236 shift = ceil (fabs (shift)) * sign (shift);
1238 return scm_from_double (offset + staff_space * shift);
1241 MAKE_SCHEME_CALLBACK_WITH_OPTARGS (Beam, pure_rest_collision_callback, 4, 1, "");
1243 Beam::pure_rest_collision_callback (SCM smob,
1244 SCM, /* prev_offset */
1250 Grob *me = unsmob_grob (smob);
1251 Grob *stem = unsmob_grob (me->get_object ("stem"));
1253 return scm_from_double (amount);
1254 Grob *beam = unsmob_grob (stem->get_object ("beam"));
1256 || !Beam::normal_stem_count (beam))
1257 return scm_from_double (amount);
1259 Real ss = Staff_symbol_referencer::staff_space (me);
1262 This gives the extrema of rest positions.
1263 In general, beams are never typeset more than one staff space away
1264 from the staff in either direction.
1266 Grob *staff = Staff_symbol_referencer::get_staff_symbol (me);
1267 Interval rest_max_pos = staff ? Staff_symbol::line_span (staff) : Interval (0.0, 0.0);
1268 rest_max_pos.widen (1);
1269 rest_max_pos *= ss / 2;
1271 extract_grob_set (beam, "stems", stems);
1272 vector<Grob *> my_stems;
1274 for (vsize i = 0; i < stems.size (); i++)
1275 if (Stem::head_count (stems[i]) || stems[i] == stem)
1276 my_stems.push_back (stems[i]);
1280 for (vsize i = 0; i < my_stems.size (); i++)
1281 if (my_stems[i] == stem)
1289 if (idx == (vsize)-1 || my_stems.size () == 1)
1290 return scm_from_double (amount);
1292 left = right = my_stems[1];
1293 else if (idx == my_stems.size () - 1)
1294 left = right = my_stems[idx - 1];
1297 left = my_stems[idx - 1];
1298 right = my_stems[idx + 1];
1300 Direction beamdir = get_grob_direction (beam);
1302 Take the position between the two bounding head_positions,
1303 then bound it by the minimum and maximum positions outside the staff.
1304 4.0 = 2.0 to get out of staff space * 2.0 for the average
1306 amount = min (max ((Stem::head_positions (left)[beamdir] + Stem::head_positions (right)[beamdir]) / 4.0, rest_max_pos[DOWN]), rest_max_pos[UP]);
1308 return scm_from_double (amount);
1313 Beam::is_knee (Grob *me)
1315 SCM k = me->get_property ("knee");
1316 if (scm_is_bool (k))
1317 return ly_scm2bool (k);
1321 extract_grob_set (me, "stems", stems);
1322 for (vsize i = stems.size (); i--;)
1324 Direction dir = get_grob_direction (stems[i]);
1333 me->set_property ("knee", ly_bool2scm (knee));
1339 Beam::is_cross_staff (Grob *me)
1341 extract_grob_set (me, "stems", stems);
1342 Grob *staff_symbol = Staff_symbol_referencer::get_staff_symbol (me);
1343 for (vsize i = 0; i < stems.size (); i++)
1344 if (Staff_symbol_referencer::get_staff_symbol (stems[i]) != staff_symbol)
1349 MAKE_SCHEME_CALLBACK (Beam, calc_cross_staff, 1)
1351 Beam::calc_cross_staff (SCM smob)
1353 return scm_from_bool (is_cross_staff (unsmob_grob (smob)));
1357 Beam::get_direction_beam_count (Grob *me, Direction d)
1359 extract_grob_set (me, "stems", stems);
1362 for (vsize i = stems.size (); i--;)
1365 Should we take invisible stems into account?
1367 if (get_grob_direction (stems[i]) == d)
1368 bc = max (bc, (Stem::beam_multiplicity (stems[i]).length () + 1));
1374 ADD_INTERFACE (Beam,
1377 "The @code{beam-thickness} property is the weight of beams,"
1378 " measured in staffspace. The @code{direction} property is"
1379 " not user-serviceable. Use the @code{direction} property"
1380 " of @code{Stem} instead.\n"
1382 "The following properties may be set in the @code{details}"
1386 "@item stem-length-demerit-factor\n"
1387 "Demerit factor used for inappropriate stem lengths.\n"
1388 "@item secondary-beam-demerit\n"
1389 "Demerit used in quanting calculations for multiple"
1391 "@item region-size\n"
1392 "Size of region for checking quant scores.\n"
1394 "Epsilon for beam quant code to check for presence"
1396 "@item stem-length-limit-penalty\n"
1397 "Penalty for differences in stem lengths on a beam.\n"
1398 "@item damping-direction-penalty\n"
1399 "Demerit penalty applied when beam direction is different"
1400 " from damping direction.\n"
1401 "@item hint-direction-penalty\n"
1402 "Demerit penalty applied when beam direction is different"
1403 " from damping direction, but damping slope is"
1404 " <= @code{round-to-zero-slope}.\n"
1405 "@item musical-direction-factor\n"
1406 "Demerit scaling factor for difference between"
1407 " beam slope and music slope.\n"
1408 "@item ideal-slope-factor\n"
1409 "Demerit scaling factor for difference between"
1410 " beam slope and damping slope.\n"
1411 "@item round-to-zero-slope\n"
1412 "Damping slope which is considered zero for purposes of"
1413 " calculating direction penalties.\n"
1419 "beamed-stem-shorten "
1425 "consistent-broken-slope "
1426 "collision-interfaces "
1427 "collision-voice-only "
1439 "neutral-direction "
1442 "quantized-positions "