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 "beam-scoring-problem.hh"
41 #include "beaming-pattern.hh"
42 #include "directional-element-interface.hh"
43 #include "grob-array.hh"
44 #include "international.hh"
45 #include "interval-set.hh"
47 #include "least-squares.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-referencer.hh"
60 #if DEBUG_BEAM_SCORING
61 #include "text-interface.hh" // debug output.
62 #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);
220 /* We want a maximal number of shared beams, but if there is choice, we
221 * take the one that is closest to the end of the stem. This is for
233 position_with_maximal_common_beams (SCM left_beaming, SCM right_beaming,
237 Slice lslice = int_list_to_slice (scm_cdr (left_beaming));
241 for (int i = lslice[-left_dir];
242 (i - lslice[left_dir]) * left_dir <= 0; i += left_dir)
245 for (SCM s = scm_car (right_beaming); scm_is_pair (s); s = scm_cdr (s))
247 int k = -right_dir * scm_to_int (scm_car (s)) + i;
248 if (scm_c_memq (scm_from_int (k), left_beaming) != SCM_BOOL_F)
252 if (count >= best_count)
262 MAKE_SCHEME_CALLBACK (Beam, calc_beaming, 1)
264 Beam::calc_beaming (SCM smob)
266 Grob *me = unsmob_grob (smob);
268 extract_grob_set (me, "stems", stems);
271 last_int.set_empty ();
273 SCM last_beaming = scm_cons (SCM_EOL, scm_list_1 (scm_from_int (0)));
274 Direction last_dir = CENTER;
275 for (vsize i = 0; i < stems.size (); i++)
277 Grob *this_stem = stems[i];
278 SCM this_beaming = this_stem->get_property ("beaming");
280 Direction this_dir = get_grob_direction (this_stem);
281 if (scm_is_pair (last_beaming) && scm_is_pair (this_beaming))
283 int start_point = position_with_maximal_common_beams
284 (last_beaming, this_beaming,
285 last_dir ? last_dir : this_dir,
292 new_slice.set_empty ();
293 SCM s = index_get_cell (this_beaming, d);
294 for (; scm_is_pair (s); s = scm_cdr (s))
297 = start_point - this_dir * scm_to_int (scm_car (s));
299 new_slice.add_point (new_beam_pos);
300 scm_set_car_x (s, scm_from_int (new_beam_pos));
303 while (flip (&d) != LEFT);
305 if (!new_slice.is_empty ())
306 last_int = new_slice;
311 FIXME: what's this for?
313 SCM s = scm_cdr (this_beaming);
314 for (; scm_is_pair (s); s = scm_cdr (s))
316 int np = -this_dir * scm_to_int (scm_car (s));
317 scm_set_car_x (s, scm_from_int (np));
318 last_int.add_point (np);
322 if (scm_ilength (scm_cdr (this_beaming)) > 0)
324 last_beaming = this_beaming;
333 operator <(Beam_stem_segment const &a,
334 Beam_stem_segment const &b)
336 return a.rank_ < b.rank_;
339 typedef map<int, vector<Beam_stem_segment> > Position_stem_segments_map;
341 // TODO - should store result in a property?
343 Beam::get_beam_segments (Grob *me_grob, Grob **common)
345 /* ugh, this has a side-effect that we need to ensure that
346 Stem #'beaming is correct */
347 (void) me_grob->get_property ("beaming");
349 Spanner *me = dynamic_cast<Spanner*> (me_grob);
351 extract_grob_set (me, "stems", stems);
352 Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
354 commonx = me->get_bound (LEFT)->common_refpoint (commonx, X_AXIS);
355 commonx = me->get_bound (RIGHT)->common_refpoint (commonx, X_AXIS);
359 int gap_count = robust_scm2int (me->get_property ("gap-count"), 0);
360 Real gap_length = robust_scm2double (me->get_property ("gap"), 0.0);
362 Position_stem_segments_map stem_segments;
363 Real lt = me->layout ()->get_dimension (ly_symbol2scm ("line-thickness"));
365 /* There are two concepts of "rank" that are used in the following code.
366 The beam_rank is the vertical position of the beam (larger numbers are
367 closer to the noteheads). Beam_stem_segment.rank_, on the other hand,
368 is the horizontal position of the segment (this is incremented by two
369 for each stem; the beam segment on the right side of the stem has
370 a higher rank (by one) than its neighbour to the left). */
372 for (vsize i = 0; i < stems.size (); i++)
374 Grob *stem = stems[i];
375 Real stem_width = robust_scm2double (stem->get_property ("thickness"), 1.0) * lt;
376 Real stem_x = stem->relative_coordinate (commonx, X_AXIS);
377 SCM beaming = stem->get_property ("beaming");
381 // Find the maximum and minimum beam ranks.
382 // Given that RANKS is never reset to empty, the interval will always be
383 // smallest for the left beamlet of the first stem, and then it might grow.
384 // Do we really want this? (It only affects the tremolo gaps) --jneem
385 for (SCM s = index_get_cell (beaming, d);
386 scm_is_pair (s); s = scm_cdr (s))
388 if (!scm_is_integer (scm_car (s)))
391 int beam_rank = scm_to_int (scm_car (s));
392 ranks.add_point (beam_rank);
395 for (SCM s = index_get_cell (beaming, d);
396 scm_is_pair (s); s = scm_cdr (s))
398 if (!scm_is_integer (scm_car (s)))
401 int beam_rank = scm_to_int (scm_car (s));
402 Beam_stem_segment seg;
404 seg.stem_x_ = stem_x;
405 seg.rank_ = 2 * i + (d+1)/2;
406 seg.width_ = stem_width;
409 seg.max_connect_ = robust_scm2int (stem->get_property ("max-beam-connect"), 1000);
411 Direction stem_dir = get_grob_direction (stem);
414 = (stem_dir * beam_rank < (stem_dir * ranks[-stem_dir] + gap_count));
415 stem_segments[beam_rank].push_back (seg);
418 while (flip (&d) != LEFT);
421 Drul_array<Real> break_overshoot
422 = robust_scm2drul (me->get_property ("break-overshoot"),
423 Drul_array<Real> (-0.5, 0.0));
425 vector<Beam_segment> segments;
426 for (Position_stem_segments_map::const_iterator i (stem_segments.begin ());
427 i != stem_segments.end (); i++)
429 vector<Beam_stem_segment> segs = (*i).second;
430 vector_sort (segs, less<Beam_stem_segment> ());
432 Beam_segment current;
434 // Iterate over all of the segments of the current beam rank,
435 // merging the adjacent Beam_stem_segments into one Beam_segment
437 int vertical_count = (*i).first;
438 for (vsize j = 0; j < segs.size (); j++)
440 // Keeping track of the different directions here is a little tricky.
441 // segs[j].dir_ is the direction of the beam segment relative to the stem
442 // (ie. segs[j].dir_ == LEFT if the beam segment sticks out to the left of
443 // its stem) whereas event_dir refers to the edge of the beam segment that
444 // we are currently looking at (ie. if segs[j].dir_ == event_dir then we
445 // are looking at that edge of the beam segment that is furthest from its
447 Direction event_dir = LEFT;
448 Beam_stem_segment const& seg = segs[j];
451 Beam_stem_segment const& neighbor_seg = segs[j + event_dir];
452 // TODO: make names clearer? --jneem
453 // on_line_bound: whether the current segment is on the boundary of the WHOLE beam
454 // on_beam_bound: whether the current segment is on the boundary of just that part
455 // of the beam with the current beam_rank
456 bool on_line_bound = (seg.dir_ == LEFT) ? seg.stem_index_ == 0
457 : seg.stem_index_ == stems.size() - 1;
458 bool on_beam_bound = (event_dir == LEFT) ? j == 0 :
459 j == segs.size () - 1;
460 bool inside_stem = (event_dir == LEFT)
461 ? seg.stem_index_ > 0
462 : seg.stem_index_ + 1 < stems.size () ;
464 bool event = on_beam_bound
465 || abs (seg.rank_ - neighbor_seg.rank_) > 1
466 || (abs (vertical_count) >= seg.max_connect_
467 || abs (vertical_count) >= neighbor_seg.max_connect_);
470 // Then this edge of the current segment is irrelevent because it will
471 // be connected with the next segment in the event_dir direction.
474 current.vertical_count_ = vertical_count;
475 current.horizontal_[event_dir] = seg.stem_x_;
476 if (seg.dir_ == event_dir)
477 // then we are examining the edge of a beam segment that is furthest
481 && me->get_bound (event_dir)->break_status_dir ())
483 current.horizontal_[event_dir]
484 = (robust_relative_extent (me->get_bound (event_dir),
485 commonx, X_AXIS)[RIGHT]
486 + event_dir * break_overshoot[event_dir]);
490 Grob *stem = stems[seg.stem_index_];
491 Drul_array<Real> beamlet_length =
492 robust_scm2interval (stem->get_property ("beamlet-default-length"), Interval (1.1, 1.1));
493 Drul_array<Real> max_proportion =
494 robust_scm2interval (stem->get_property ("beamlet-max-length-proportion"), Interval (0.75, 0.75));
495 Real length = beamlet_length[seg.dir_];
499 Grob *neighbor_stem = stems[seg.stem_index_ + event_dir];
500 Real neighbor_stem_x = neighbor_stem->relative_coordinate (commonx, X_AXIS);
502 length = min (length,
503 fabs (neighbor_stem_x - seg.stem_x_) * max_proportion[seg.dir_]);
505 current.horizontal_[event_dir] += event_dir * length;
509 // we are examining the edge of a beam segment that is closest
510 // (ie. touching, unless there is a gap) its stem.
512 current.horizontal_[event_dir] += event_dir * seg.width_/2;
515 current.horizontal_[event_dir] -= event_dir * gap_length;
517 if (Stem::is_invisible (seg.stem_))
520 Need to do this in case of whole notes. We don't want the
521 heads to collide with the beams.
523 extract_grob_set (seg.stem_, "note-heads", heads);
525 for (vsize k = 0; k < heads.size (); k ++)
526 current.horizontal_[event_dir]
527 = event_dir * min (event_dir * current.horizontal_[event_dir],
530 * heads[k]->extent (commonx,
531 X_AXIS)[-event_dir]);
536 if (event_dir == RIGHT)
538 segments.push_back (current);
539 current = Beam_segment ();
542 while (flip (&event_dir) != LEFT);
550 MAKE_SCHEME_CALLBACK (Beam, print, 1);
552 Beam::print (SCM grob)
554 Spanner *me = unsmob_spanner (grob);
556 vector<Beam_segment> segments = get_beam_segments (me, &commonx);
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"));
594 for (vsize i = 0; i < segments.size (); i ++)
596 Real local_slope = slope;
599 local_slope += feather_dir * segments[i].vertical_count_ * beam_dy / span.length ();
602 Stencil b = Lookup::beam (local_slope, segments[i].horizontal_.length (), beam_thickness, blot);
604 b.translate_axis (segments[i].horizontal_[LEFT], X_AXIS);
606 b.translate_axis (local_slope
607 * (segments[i].horizontal_[LEFT] - span.linear_combination (feather_dir))
608 + pos.linear_combination (feather_dir)
609 + beam_dy * segments[i].vertical_count_, Y_AXIS);
610 the_beam.add_stencil (b);
613 #if (DEBUG_BEAM_SCORING)
614 SCM annotation = me->get_property ("annotation");
615 if (scm_is_string (annotation))
617 extract_grob_set (me, "stems", stems);
620 This code prints the demerits for each beam. Perhaps this
621 should be switchable for those who want to twiddle with the
625 SCM properties = Font_interface::text_font_alist_chain (me);
627 properties = scm_cons(scm_acons (ly_symbol2scm ("font-size"), scm_from_int (-5), SCM_EOL),
630 Direction stem_dir = stems.size () ? to_dir (stems[0]->get_property ("direction")) : UP;
632 Stencil score = *unsmob_stencil (Text_interface::interpret_markup
633 (me->layout ()->self_scm (), properties, annotation));
635 if (!score.is_empty ())
637 score.translate_axis (me->relative_coordinate(commonx, X_AXIS), X_AXIS);
638 the_beam.add_at_edge (Y_AXIS, stem_dir, score, 1.0);
643 the_beam.translate_axis (-me->relative_coordinate (commonx, X_AXIS), X_AXIS);
644 return the_beam.smobbed_copy ();
648 Beam::get_default_dir (Grob *me)
650 extract_grob_set (me, "stems", stems);
652 Drul_array<Real> extremes (0.0, 0.0);
653 for (iterof (s, stems); s != stems.end (); s++)
655 Interval positions = Stem::head_positions (*s);
659 if (sign (positions[d]) == d)
660 extremes[d] = d * max (d * positions[d], d * extremes[d]);
662 while (flip (&d) != DOWN);
665 Drul_array<int> total (0, 0);
666 Drul_array<int> count (0, 0);
668 bool force_dir = false;
669 for (vsize i = 0; i < stems.size (); i++)
672 Direction stem_dir = CENTER;
673 SCM stem_dir_scm = s->get_property_data ("direction");
674 if (is_direction (stem_dir_scm))
676 stem_dir = to_dir (stem_dir_scm);
680 stem_dir = to_dir (s->get_property ("default-direction"));
683 stem_dir = to_dir (s->get_property ("neutral-direction"));
688 total[stem_dir] += max (int (- stem_dir * Stem::head_positions (s) [-stem_dir]), 0);
695 if (abs (extremes[UP]) > -extremes[DOWN])
697 else if (extremes[UP] < -extremes[DOWN])
701 Direction dir = CENTER;
702 Direction d = CENTER;
703 if ((d = (Direction) sign (count[UP] - count[DOWN])))
707 && (d = (Direction) sign (total[UP] / count[UP] - total[DOWN]/count[DOWN])))
709 else if ((d = (Direction) sign (total[UP] - total[DOWN])))
712 dir = to_dir (me->get_property ("neutral-direction"));
717 /* Set all stems with non-forced direction to beam direction.
718 Urg: non-forced should become `without/with unforced' direction,
719 once stem gets cleaned-up. */
721 Beam::set_stem_directions (Grob *me, Direction d)
723 extract_grob_set (me, "stems", stems);
725 for (vsize i = 0; i < stems.size (); i++)
729 SCM forcedir = s->get_property_data ("direction");
730 if (!to_dir (forcedir))
731 set_grob_direction (s, d);
736 Only try horizontal beams for knees. No reliable detection of
737 anything else is possible here, since we don't know funky-beaming
738 settings, or X-distances (slopes!) People that want sloped
739 knee-beams, should set the directions manually.
744 this routine should take into account the stemlength scoring
745 of a possible knee/nonknee beam.
748 Beam::consider_auto_knees (Grob *me)
750 SCM scm = me->get_property ("auto-knee-gap");
751 if (!scm_is_number (scm))
758 extract_grob_set (me, "normal-stems", stems);
760 Grob *common = common_refpoint_of_array (stems, me, Y_AXIS);
761 Real staff_space = Staff_symbol_referencer::staff_space (me);
763 vector<Interval> head_extents_array;
764 for (vsize i = 0; i < stems.size (); i++)
766 Grob *stem = stems[i];
768 Interval head_extents = Stem::head_positions (stem);
769 if (!head_extents.is_empty ())
771 head_extents[LEFT] += -1;
772 head_extents[RIGHT] += 1;
773 head_extents *= staff_space * 0.5;
776 We could subtract beam Y position, but this routine only
777 sets stem directions, a constant shift does not have an
780 head_extents += stem->pure_relative_y_coordinate (common, 0, INT_MAX);
782 if (to_dir (stem->get_property_data ("direction")))
784 Direction stemdir = to_dir (stem->get_property ("direction"));
785 head_extents[-stemdir] = -stemdir * infinity_f;
788 head_extents_array.push_back (head_extents);
790 gaps.remove_interval (head_extents);
794 Real max_gap_len = 0.0;
796 for (vsize i = gaps.allowed_regions_.size () -1; i != VPOS ;i--)
798 Interval gap = gaps.allowed_regions_[i];
801 the outer gaps are not knees.
803 if (isinf (gap[LEFT]) || isinf (gap[RIGHT]))
806 if (gap.length () >= max_gap_len)
808 max_gap_len = gap.length ();
813 Real beam_translation = get_beam_translation (me);
814 Real beam_thickness = Beam::get_beam_thickness (me);
815 int beam_count = Beam::get_beam_count (me);
816 Real height_of_beams = beam_thickness / 2
817 + (beam_count - 1) * beam_translation;
818 Real threshold = scm_to_double (scm) + height_of_beams;
820 if (max_gap_len > threshold)
823 for (vsize i = 0; i < stems.size (); i++)
825 Grob *stem = stems[i];
826 Interval head_extents = head_extents_array[j++];
828 Direction d = (head_extents.center () < max_gap.center ())
831 stem->set_property ("direction", scm_from_int (d));
833 head_extents.intersect (max_gap);
834 assert (head_extents.is_empty () || head_extents.length () < 1e-6);
839 /* Set stem's shorten property if unset.
842 take some y-position (chord/beam/nearest?) into account
843 scmify forced-fraction
845 This is done in beam because the shorten has to be uniform over the
852 set_minimum_dy (Grob *me, Real *dy)
857 If dy is smaller than the smallest quant, we
858 get absurd direction-sign penalties.
861 Real ss = Staff_symbol_referencer::staff_space (me);
862 Real beam_thickness = Beam::get_beam_thickness (me) / ss;
863 Real slt = Staff_symbol_referencer::line_thickness (me) / ss;
864 Real sit = (beam_thickness - slt) / 2;
866 Real hang = 1.0 - (beam_thickness - slt) / 2;
868 *dy = sign (*dy) * max (fabs (*dy),
869 min (min (sit, inter), hang));
875 MAKE_SCHEME_CALLBACK (Beam, calc_stem_shorten, 1)
877 Beam::calc_stem_shorten (SCM smob)
879 Grob *me = unsmob_grob (smob);
882 shortening looks silly for x staff beams
885 return scm_from_int (0);
887 Real forced_fraction = 1.0 * forced_stem_count (me)
888 / normal_stem_count (me);
890 int beam_count = get_beam_count (me);
892 SCM shorten_list = me->get_property ("beamed-stem-shorten");
893 if (shorten_list == SCM_EOL)
894 return scm_from_int (0);
896 Real staff_space = Staff_symbol_referencer::staff_space (me);
899 = robust_list_ref (beam_count -1, shorten_list);
900 Real shorten = scm_to_double (shorten_elt) * staff_space;
902 shorten *= forced_fraction;
906 return scm_from_double (shorten);
908 return scm_from_double (0.0);
913 Beam::no_visible_stem_positions (Grob *me, Interval default_value)
915 extract_grob_set (me, "stems", stems);
917 return default_value;
919 Interval head_positions;
921 for (vsize i = 0; i < stems.size(); i++)
923 head_positions.unite (Stem::head_positions (stems[i]));
924 multiplicity.unite (Stem::beam_multiplicity (stems[i]));
927 Direction dir = get_grob_direction (me);
928 Real y = head_positions[dir]
929 * 0.5 * Staff_symbol_referencer::staff_space (me)
930 + dir * get_beam_translation (me) * (multiplicity.length () + 1);
932 y /= Staff_symbol_referencer::staff_space (me);
933 return Interval (y,y);
938 Compute a first approximation to the beam slope.
940 MAKE_SCHEME_CALLBACK (Beam, calc_least_squares_positions, 2);
942 Beam::calc_least_squares_positions (SCM smob, SCM /* posns */)
944 Grob *me = unsmob_grob (smob);
946 int count = normal_stem_count (me);
949 return ly_interval2scm (no_visible_stem_positions (me, pos));
951 vector<Real> x_posns;
952 extract_grob_set (me, "normal-stems", stems);
953 Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
954 Grob *commony = common_refpoint_of_array (stems, me, Y_AXIS);
956 Real my_y = me->relative_coordinate (commony, Y_AXIS);
958 Grob *fvs = first_normal_stem (me);
959 Grob *lvs = last_normal_stem (me);
961 Interval ideal (Stem::get_stem_info (fvs).ideal_y_
962 + fvs->relative_coordinate (commony, Y_AXIS) - my_y,
963 Stem::get_stem_info (lvs).ideal_y_
964 + lvs->relative_coordinate (commony, Y_AXIS) - my_y);
966 Real x0 = first_normal_stem (me)->relative_coordinate (commonx, X_AXIS);
967 for (vsize i = 0; i < stems.size (); i++)
971 Real x = s->relative_coordinate (commonx, X_AXIS) - x0;
972 x_posns.push_back (x);
974 Real dx = last_normal_stem (me)->relative_coordinate (commonx, X_AXIS) - x0;
982 Interval chord (Stem::chord_start_y (stems[0]),
983 Stem::chord_start_y (stems.back ()));
985 /* Simple beams (2 stems) on middle line should be allowed to be
988 However, if both stems reach middle line,
989 ideal[LEFT] == ideal[RIGHT] and ideal.delta () == 0.
991 For that case, we apply artificial slope */
992 if (!ideal[LEFT] && chord.delta () && count == 2)
995 Direction d = (Direction) (sign (chord.delta ()) * UP);
996 pos[d] = get_beam_thickness (me) / 2;
1003 For broken beams this doesn't work well. In this case, the
1004 slope esp. of the first part of a broken beam should predict
1005 where the second part goes.
1007 ldy = pos[RIGHT] - pos[LEFT];
1011 vector<Offset> ideals;
1012 for (vsize i = 0; i < stems.size (); i++)
1015 ideals.push_back (Offset (x_posns[i],
1016 Stem::get_stem_info (s).ideal_y_
1017 + s->relative_coordinate (commony, Y_AXIS)
1021 minimise_least_squares (&slope, &y, ideals);
1025 set_minimum_dy (me, &dy);
1028 pos = Interval (y, (y + dy));
1032 "position" is relative to the staff.
1034 scale_drul (&pos, 1 / Staff_symbol_referencer::staff_space (me));
1036 me->set_property ("least-squares-dy", scm_from_double (ldy));
1037 return ly_interval2scm (pos);
1041 // Assuming V is not empty, pick a 'reasonable' point inside V.
1043 point_in_interval (Interval v, Real dist)
1045 if (isinf (v[DOWN]))
1046 return v[UP] - dist;
1047 else if (isinf (v[UP]))
1048 return v[DOWN] + dist;
1054 We can't combine with previous function, since check concave and
1055 slope damping comes first.
1057 TODO: we should use the concaveness to control the amount of damping
1060 MAKE_SCHEME_CALLBACK (Beam, shift_region_to_valid, 2);
1062 Beam::shift_region_to_valid (SCM grob, SCM posns)
1064 Grob *me = unsmob_grob (grob);
1069 vector<Real> x_posns;
1070 extract_grob_set (me, "stems", stems);
1071 extract_grob_set (me, "covered-grobs", covered);
1073 Grob *common[NO_AXES] = { me, me };
1074 for (Axis a = X_AXIS; a < NO_AXES; incr (a)) {
1075 common[a] = common_refpoint_of_array (stems, me, a);
1076 common[a] = common_refpoint_of_array (covered, common[a], a);
1078 Grob *fvs = first_normal_stem (me);
1083 x_span[LEFT] = fvs->relative_coordinate (common[X_AXIS], X_AXIS);
1084 for (vsize i = 0; i < stems.size (); i++)
1088 Real x = s->relative_coordinate (common[X_AXIS], X_AXIS) - x_span[LEFT];
1089 x_posns.push_back (x);
1092 Grob *lvs = last_normal_stem (me);
1093 x_span[RIGHT] = lvs->relative_coordinate (common[X_AXIS], X_AXIS);
1095 Drul_array<Real> pos = ly_scm2interval (posns);
1097 scale_drul (&pos, Staff_symbol_referencer::staff_space (me));
1099 Real beam_dy = pos[RIGHT] - pos[LEFT];
1100 Real beam_left_y = pos[LEFT];
1101 Real slope = x_span.delta () ? (beam_dy / x_span.delta ()) : 0.0;
1104 Shift the positions so that we have a chance of finding good
1105 quants (i.e. no short stem failures.)
1107 Interval feasible_left_point;
1108 feasible_left_point.set_full ();
1110 for (vsize i = 0; i < stems.size (); i++)
1113 if (Stem::is_invisible (s))
1116 Direction d = get_grob_direction (s);
1118 = Stem::get_stem_info (s).shortest_y_
1119 - slope * x_posns [i];
1122 left_y is now relative to the stem S. We want relative to
1123 ourselves, so translate:
1126 += + s->relative_coordinate (common[Y_AXIS], Y_AXIS)
1127 - me->relative_coordinate (common[Y_AXIS], Y_AXIS);
1133 feasible_left_point.intersect (flp);
1137 We have two intervals here, one for the up variant (beams goes
1138 over the collision) one for the down.
1140 Drul_array<Interval> collision_free (feasible_left_point,
1141 feasible_left_point);
1143 vector<Grob*> filtered;
1145 We only update these for objects that are too large for quanting
1146 to find a workaround. Typically, these are notes with
1147 stems, and timesig/keysig/clef, which take out the entire area
1148 inside the staff as feasible.
1150 The code below disregards the thickness and multiplicity of the
1151 beam. This should not be a problem, as the beam quanting will
1152 take care of computing the impact those exactly.
1154 Real min_y_size = 2.0;
1155 for (vsize i = 0; i < covered.size(); i++)
1157 if (!covered[i]->is_live())
1161 for (Axis a = X_AXIS; a < NO_AXES; incr (a))
1162 b[a] = covered[i]->extent (common[a], a);
1164 if (b[X_AXIS].is_empty () || b[Y_AXIS].is_empty ())
1167 if (intersection (b[X_AXIS], x_span).is_empty ())
1170 filtered.push_back (covered[i]);
1171 Grob *head_stem = Rhythmic_head::get_stem (covered[i]);
1172 if (head_stem && Stem::is_normal_stem (head_stem)
1173 && Note_head::has_interface (covered[i]))
1175 if (Stem::get_beam (head_stem))
1178 We must assume that stems are infinitely long in this
1179 case, as asking for the length of the stem typically
1180 leads to circular dependencies.
1182 This strategy assumes that we don't want to handle the
1183 collision of beams in opposite non-forced directions
1184 with this code, where shortening the stems of both
1185 would resolve the problem, eg.
1195 Such beams would need a coordinating grob to resolve
1196 the collision, since both will likely want to occupy
1199 Direction stemdir = get_grob_direction (head_stem);
1200 b[Y_AXIS][stemdir] = stemdir * infinity_f;
1204 // TODO - should we include the extent of the stem here?
1208 if (b[Y_AXIS].length () < min_y_size)
1214 Real x = b[X_AXIS][d] - x_span[LEFT];
1215 Real dy = slope * x;
1217 Direction yd = DOWN;
1220 Real left_y = b[Y_AXIS][yd];
1222 if (left_y == yd * infinity_f)
1224 collision_free[yd].set_empty ();
1230 // Translate back to beam as ref point.
1231 left_y -= -me->relative_coordinate (common[Y_AXIS], Y_AXIS);
1234 allowed.set_full ();
1236 allowed[-yd] = left_y;
1237 collision_free[yd].intersect (allowed);
1239 while (flip (&yd) != DOWN);
1241 while (flip (&d) != LEFT);
1245 Pointer_group_interface::get_grob_array (me,
1246 ly_symbol2scm ("covered-grobs"));
1247 arr->set_array (filtered);
1249 if (collision_free[DOWN].contains (beam_left_y)
1250 || collision_free[UP].contains (beam_left_y))
1252 // We're good to go. Do nothing.
1254 else if (!collision_free[DOWN].is_empty ()
1255 || !collision_free[UP].is_empty ())
1257 // We have space above or below collisions (or, no collisions at
1260 (collision_free[DOWN].length () > collision_free[UP].length ()) ?
1261 collision_free[DOWN] : collision_free[UP];
1263 beam_left_y = point_in_interval (best, 2.0);
1265 else if (!feasible_left_point.is_empty ())
1267 // We are somewhat screwed: we have a collision, but at least
1268 // there is a way to satisfy stem length constraints.
1269 beam_left_y = point_in_interval (feasible_left_point, 2.0);
1273 // We are completely screwed.
1274 warning (_ ("no viable initial configuration found: may not find good beam slope"));
1277 pos = Drul_array<Real> (beam_left_y, (beam_left_y + beam_dy));
1278 scale_drul (&pos, 1 / Staff_symbol_referencer::staff_space (me));
1280 return ly_interval2scm (pos);
1283 /* This neat trick is by Werner Lemberg,
1284 damped = tanh (slope)
1285 corresponds with some tables in [Wanske] CHECKME */
1286 MAKE_SCHEME_CALLBACK (Beam, slope_damping, 2);
1288 Beam::slope_damping (SCM smob, SCM posns)
1290 Grob *me = unsmob_grob (smob);
1291 Drul_array<Real> pos = ly_scm2interval (posns);
1293 if (normal_stem_count (me) <= 1)
1296 SCM s = me->get_property ("damping");
1297 Real damping = scm_to_double (s);
1298 Real concaveness = robust_scm2double (me->get_property ("concaveness"), 0.0);
1299 if (concaveness >= 10000)
1301 pos[LEFT] = pos[RIGHT];
1302 me->set_property ("least-squares-dy", scm_from_double (0));
1308 scale_drul (&pos, Staff_symbol_referencer::staff_space (me));
1310 Real dy = pos[RIGHT] - pos[LEFT];
1312 Grob *fvs = first_normal_stem (me);
1313 Grob *lvs = last_normal_stem (me);
1315 Grob *commonx = fvs->common_refpoint (lvs, X_AXIS);
1317 Real dx = last_normal_stem (me)->relative_coordinate (commonx, X_AXIS)
1318 - first_normal_stem (me)->relative_coordinate (commonx, X_AXIS);
1320 Real slope = dy && dx ? dy / dx : 0;
1322 slope = 0.6 * tanh (slope) / (damping + concaveness);
1324 Real damped_dy = slope * dx;
1326 set_minimum_dy (me, &damped_dy);
1328 pos[LEFT] += (dy - damped_dy) / 2;
1329 pos[RIGHT] -= (dy - damped_dy) / 2;
1331 scale_drul (&pos, 1 / Staff_symbol_referencer::staff_space (me));
1334 return ly_interval2scm (pos);
1338 MAKE_SCHEME_CALLBACK (Beam, quanting, 2);
1340 Beam::quanting (SCM smob, SCM posns)
1342 Grob *me = unsmob_grob (smob);
1343 Drul_array<Real> ys(0, 0);
1344 ys = robust_scm2drul (posns, ys);
1345 Beam_scoring_problem problem (me, ys);
1347 ys = problem.solve ();
1348 return ly_interval2scm (ys);
1353 Report slice containing the numbers that are both in (car BEAMING)
1357 where_are_the_whole_beams (SCM beaming)
1361 for (SCM s = scm_car (beaming); scm_is_pair (s); s = scm_cdr (s))
1363 if (scm_c_memq (scm_car (s), scm_cdr (beaming)) != SCM_BOOL_F)
1365 l.add_point (scm_to_int (scm_car (s)));
1371 /* Return the Y position of the stem-end, given the Y-left, Y-right
1372 in POS for stem S. This Y position is relative to S. */
1374 Beam::calc_stem_y (Grob *me, Grob *stem, Grob **common,
1375 Real xl, Real xr, Direction feather_dir,
1376 Drul_array<Real> pos, bool french)
1378 Real beam_translation = get_beam_translation (me);
1379 Direction stem_dir = get_grob_direction (stem);
1382 Real relx = dx ? (stem->relative_coordinate (common[X_AXIS], X_AXIS) - xl)/dx : 0;
1383 Real xdir = 2*relx-1;
1385 Real stem_y = linear_combination(pos, xdir);
1387 SCM beaming = stem->get_property ("beaming");
1389 Slice beam_slice (french
1390 ? where_are_the_whole_beams (beaming)
1391 : Stem::beam_multiplicity (stem));
1392 if (beam_slice.is_empty ())
1393 beam_slice = Slice (0,0);
1394 Interval beam_multiplicity(beam_slice[LEFT],
1398 feather dir = 1 , relx 0->1 : factor 0 -> 1
1399 feather dir = 0 , relx 0->1 : factor 1 -> 1
1400 feather dir = -1, relx 0->1 : factor 1 -> 0
1402 Real feather_factor = 1;
1403 if (feather_dir > 0)
1404 feather_factor = relx;
1405 else if (feather_dir < 0)
1406 feather_factor = 1 - relx;
1408 stem_y += feather_factor * beam_translation
1409 * beam_multiplicity[Direction(((french) ? DOWN : UP)*stem_dir)];
1410 Real id = me->relative_coordinate (common[Y_AXIS], Y_AXIS)
1411 - stem->relative_coordinate (common[Y_AXIS], Y_AXIS);
1417 Hmm. At this time, beam position and slope are determined. Maybe,
1418 stem directions and length should set to relative to the chord's
1419 position of the beam. */
1420 MAKE_SCHEME_CALLBACK (Beam, set_stem_lengths, 1);
1422 Beam::set_stem_lengths (SCM smob)
1424 Grob *me = unsmob_grob (smob);
1426 /* trigger callbacks. */
1427 (void) me->get_property ("direction");
1428 (void) me->get_property ("beaming");
1430 SCM posns = me->get_property ("positions");
1432 extract_grob_set (me, "stems", stems);
1437 for (int a = 2; a--;)
1438 common[a] = common_refpoint_of_array (stems, me, Axis (a));
1440 Drul_array<Real> pos = ly_scm2realdrul (posns);
1441 Real staff_space = Staff_symbol_referencer::staff_space (me);
1442 scale_drul (&pos, staff_space);
1446 if (robust_scm2int (me->get_property ("gap-count"), 0))
1449 thick = get_beam_thickness (me);
1452 Grob *fvs = first_normal_stem (me);
1453 Grob *lvs = last_normal_stem (me);
1455 Real xl = fvs ? fvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
1456 Real xr = lvs ? lvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
1457 Direction feather_dir = to_dir (me->get_property ("grow-direction"));
1459 for (vsize i = 0; i < stems.size (); i++)
1463 bool french = to_boolean (s->get_property ("french-beaming"));
1464 Real stem_y = calc_stem_y (me, s, common,
1465 xl, xr, feather_dir,
1466 pos, french && s != lvs && s!= fvs);
1469 Make the stems go up to the end of the beam. This doesn't matter
1470 for normal beams, but for tremolo beams it looks silly otherwise.
1473 && !Stem::is_invisible (s))
1474 stem_y += thick * 0.5 * get_grob_direction (s);
1477 Do set_stemend for invisible stems too, so tuplet brackets
1478 have a reference point for sloping
1480 Stem::set_stemend (s, 2 * stem_y / staff_space);
1487 Beam::set_beaming (Grob *me, Beaming_pattern const *beaming)
1489 extract_grob_set (me, "stems", stems);
1492 for (vsize i = 0; i < stems.size (); i++)
1495 Don't overwrite user settings.
1499 Grob *stem = stems[i];
1500 SCM beaming_prop = stem->get_property ("beaming");
1501 if (beaming_prop == SCM_EOL
1502 || index_get_cell (beaming_prop, d) == SCM_EOL)
1504 int count = beaming->beamlet_count (i, d);
1506 && i + 1 < stems.size ()
1507 && Stem::is_invisible (stem))
1508 count = min (count, beaming->beamlet_count (i,-d));
1510 if ( ((i == 0 && d == LEFT)
1511 || (i == stems.size ()-1 && d == RIGHT))
1512 && stems.size () > 1
1513 && to_boolean (me->get_property ("clip-edges")))
1516 Stem::set_beaming (stem, count, d);
1519 while (flip (&d) != LEFT);
1524 Beam::forced_stem_count (Grob *me)
1526 extract_grob_set (me, "normal-stems", stems);
1529 for (vsize i = 0; i < stems.size (); i++)
1533 /* I can imagine counting those boundaries as a half forced stem,
1534 but let's count them full for now. */
1535 Direction defdir = to_dir (s->get_property ("default-direction"));
1537 if (abs (Stem::chord_start_y (s)) > 0.1
1539 && get_grob_direction (s) != defdir)
1546 Beam::normal_stem_count (Grob *me)
1548 extract_grob_set (me, "normal-stems", stems);
1549 return stems.size ();
1553 Beam::first_normal_stem (Grob *me)
1555 extract_grob_set (me, "normal-stems", stems);
1556 return stems.size () ? stems[0] : 0;
1560 Beam::last_normal_stem (Grob *me)
1562 extract_grob_set (me, "normal-stems", stems);
1563 return stems.size () ? stems.back () : 0;
1569 handle rest under beam (do_post: beams are calculated now)
1570 what about combination of collisions and rest under beam.
1574 rest -> stem -> beam -> interpolate_y_position ()
1576 MAKE_SCHEME_CALLBACK_WITH_OPTARGS (Beam, rest_collision_callback, 2, 1, "");
1578 Beam::rest_collision_callback (SCM smob, SCM prev_offset)
1580 Grob *rest = unsmob_grob (smob);
1581 if (scm_is_number (rest->get_property ("staff-position")))
1582 return scm_from_int (0);
1584 Real offset = robust_scm2double (prev_offset, 0.0);
1586 Grob *st = unsmob_grob (rest->get_object ("stem"));
1589 return scm_from_double (0.0);
1590 Grob *beam = unsmob_grob (stem->get_object ("beam"));
1592 || !Beam::has_interface (beam)
1593 || !Beam::normal_stem_count (beam))
1594 return scm_from_double (0.0);
1596 Drul_array<Real> pos (robust_scm2drul (beam->get_property ("positions"),
1597 Drul_array<Real> (0,0)));
1599 Real staff_space = Staff_symbol_referencer::staff_space (rest);
1601 scale_drul (&pos, staff_space);
1603 Real dy = pos[RIGHT] - pos[LEFT];
1605 Drul_array<Grob*> visible_stems (first_normal_stem (beam),
1606 last_normal_stem (beam));
1607 extract_grob_set (beam, "stems", stems);
1609 Grob *common = common_refpoint_of_array (stems, beam, X_AXIS);
1611 Real x0 = visible_stems[LEFT]->relative_coordinate (common, X_AXIS);
1612 Real dx = visible_stems[RIGHT]->relative_coordinate (common, X_AXIS) - x0;
1613 Real slope = dy && dx ? dy / dx : 0;
1615 Direction d = get_grob_direction (stem);
1616 Real stem_y = pos[LEFT]
1617 + (stem->relative_coordinate (common, X_AXIS) - x0) * slope;
1619 Real beam_translation = get_beam_translation (beam);
1620 Real beam_thickness = Beam::get_beam_thickness (beam);
1623 TODO: this is not strictly correct for 16th knee beams.
1626 = Stem::beam_multiplicity (stem).length () + 1;
1628 Real height_of_my_beams = beam_thickness / 2
1629 + (beam_count - 1) * beam_translation;
1630 Real beam_y = stem_y - d * height_of_my_beams;
1632 Grob *common_y = rest->common_refpoint (beam, Y_AXIS);
1634 Interval rest_extent = rest->extent (rest, Y_AXIS);
1635 rest_extent.translate (offset + rest->get_parent (Y_AXIS)->relative_coordinate (common_y, Y_AXIS));
1637 Real rest_dim = rest_extent[d];
1638 Real minimum_distance
1639 = staff_space * (robust_scm2double (stem->get_property ("stemlet-length"), 0.0)
1640 + robust_scm2double (rest->get_property ("minimum-distance"), 0.0));
1642 Real shift = d * min (d * (beam_y - d * minimum_distance - rest_dim), 0.0);
1644 shift /= staff_space;
1645 Real rad = Staff_symbol_referencer::line_count (rest) * staff_space / 2;
1647 /* Always move discretely by half spaces */
1648 shift = ceil (fabs (shift * 2.0)) / 2.0 * sign (shift);
1650 /* Inside staff, move by whole spaces*/
1651 if ((rest_extent[d] + staff_space * shift) * d
1653 || (rest_extent[-d] + staff_space * shift) * -d
1655 shift = ceil (fabs (shift)) * sign (shift);
1657 return scm_from_double (offset + staff_space * shift);
1661 Beam::is_knee (Grob *me)
1663 SCM k = me->get_property ("knee");
1664 if (scm_is_bool (k))
1665 return ly_scm2bool (k);
1669 extract_grob_set (me, "stems", stems);
1670 for (vsize i = stems.size (); i--;)
1672 Direction dir = get_grob_direction (stems[i]);
1681 me->set_property ("knee", ly_bool2scm (knee));
1687 Beam::is_cross_staff (Grob *me)
1689 extract_grob_set (me, "stems", stems);
1690 Grob *staff_symbol = Staff_symbol_referencer::get_staff_symbol (me);
1691 for (vsize i = 0; i < stems.size (); i++)
1692 if (Staff_symbol_referencer::get_staff_symbol (stems[i]) != staff_symbol)
1697 MAKE_SCHEME_CALLBACK (Beam, calc_cross_staff, 1)
1699 Beam::calc_cross_staff (SCM smob)
1701 return scm_from_bool (is_cross_staff (unsmob_grob (smob)));
1705 Beam::get_direction_beam_count (Grob *me, Direction d)
1707 extract_grob_set (me, "stems", stems);
1710 for (vsize i = stems.size (); i--;)
1713 Should we take invisible stems into account?
1715 if (get_grob_direction (stems[i]) == d)
1716 bc = max (bc, (Stem::beam_multiplicity (stems[i]).length () + 1));
1722 ADD_INTERFACE (Beam,
1725 "The @code{beam-thickness} property is the weight of beams,"
1726 " measured in staffspace. The @code{direction} property is"
1727 " not user-serviceable. Use the @code{direction} property"
1728 " of @code{Stem} instead.\n"
1730 "The following properties may be set in the @code{details}"
1734 "@item stem-length-demerit-factor\n"
1735 "Demerit factor used for inappropriate stem lengths.\n"
1736 "@item secondary-beam-demerit\n"
1737 "Demerit used in quanting calculations for multiple"
1739 "@item region-size\n"
1740 "Size of region for checking quant scores.\n"
1742 "Epsilon for beam quant code to check for presence"
1744 "@item stem-length-limit-penalty\n"
1745 "Penalty for differences in stem lengths on a beam.\n"
1746 "@item damping-direction-penalty\n"
1747 "Demerit penalty applied when beam direction is different"
1748 " from damping direction.\n"
1749 "@item hint-direction-penalty\n"
1750 "Demerit penalty applied when beam direction is different"
1751 " from damping direction, but damping slope is"
1752 " <= @code{round-to-zero-slope}.\n"
1753 "@item musical-direction-factor\n"
1754 "Demerit scaling factor for difference between"
1755 " beam slope and music slope.\n"
1756 "@item ideal-slope-factor\n"
1757 "Demerit scaling factor for difference between"
1758 " beam slope and damping slope.\n"
1759 "@item round-to-zero-slope\n"
1760 "Damping slope which is considered zero for purposes of"
1761 " calculating direction penalties.\n"
1767 "beamed-stem-shorten "
1784 "neutral-direction "
1787 "quantized-positions "