X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=lily%2Fbeam.cc;h=49253434c05d94faab4e471b44cc42898841f1e9;hb=9ec7c79078944a94e2e9abbf8e1b3299c9706389;hp=67b1019376100cca3dd68586eda4979fe31024e9;hpb=1d9a73b13ee576d28c0f41f5b243f2ebb1ff9fcf;p=lilypond.git diff --git a/lily/beam.cc b/lily/beam.cc index 67b1019376..49253434c0 100644 --- a/lily/beam.cc +++ b/lily/beam.cc @@ -1,7 +1,7 @@ /* This file is part of LilyPond, the GNU music typesetter. - Copyright (C) 1997--2011 Han-Wen Nienhuys + Copyright (C) 1997--2012 Han-Wen Nienhuys Jan Nieuwenhuizen LilyPond is free software: you can redistribute it and/or modify @@ -37,6 +37,7 @@ #include "beam.hh" +#include "axis-group-interface.hh" #include "align-interface.hh" #include "beam-scoring-problem.hh" #include "beaming-pattern.hh" @@ -283,9 +284,8 @@ Beam::calc_beaming (SCM smob) last_dir ? last_dir : this_dir, this_dir); - Direction d = LEFT; Slice new_slice; - do + for (LEFT_and_RIGHT (d)) { new_slice.set_empty (); SCM s = index_get_cell (this_beaming, d); @@ -298,7 +298,6 @@ Beam::calc_beaming (SCM smob) scm_set_car_x (s, scm_from_int (new_beam_pos)); } } - while (flip (&d) != LEFT); if (!new_slice.is_empty ()) last_int = new_slice; @@ -336,23 +335,22 @@ operator <(Beam_stem_segment const &a, typedef map > Position_stem_segments_map; -// TODO - should store result in a property? -vector -Beam::get_beam_segments (Grob *me_grob, Grob **common) +MAKE_SCHEME_CALLBACK (Beam, calc_beam_segments, 1); +SCM +Beam::calc_beam_segments (SCM smob) { /* ugh, this has a side-effect that we need to ensure that Stem #'beaming is correct */ + Grob *me_grob = unsmob_grob (smob); (void) me_grob->get_property ("beaming"); Spanner *me = dynamic_cast (me_grob); extract_grob_set (me, "stems", stems); - Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS); - - commonx = me->get_bound (LEFT)->common_refpoint (commonx, X_AXIS); - commonx = me->get_bound (RIGHT)->common_refpoint (commonx, X_AXIS); - *common = commonx; + Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS); + for (LEFT_and_RIGHT (d)) + commonx = me->get_bound (d)->common_refpoint (commonx, X_AXIS); int gap_count = robust_scm2int (me->get_property ("gap-count"), 0); Real gap_length = robust_scm2double (me->get_property ("gap"), 0.0); @@ -373,8 +371,8 @@ Beam::get_beam_segments (Grob *me_grob, Grob **common) Real stem_width = robust_scm2double (stem->get_property ("thickness"), 1.0) * lt; Real stem_x = stem->relative_coordinate (commonx, X_AXIS); SCM beaming = stem->get_property ("beaming"); - Direction d = LEFT; - do + + for (LEFT_and_RIGHT (d)) { // Find the maximum and minimum beam ranks. // Given that RANKS is never reset to empty, the interval will always be @@ -413,7 +411,6 @@ Beam::get_beam_segments (Grob *me_grob, Grob **common) stem_segments[beam_rank].push_back (seg); } } - while (flip (&d) != LEFT); } Drul_array break_overshoot @@ -442,9 +439,8 @@ Beam::get_beam_segments (Grob *me_grob, Grob **common) // we are currently looking at (ie. if segs[j].dir_ == event_dir then we // are looking at that edge of the beam segment that is furthest from its // stem). - Direction event_dir = LEFT; Beam_stem_segment const &seg = segs[j]; - do + for (LEFT_and_RIGHT (event_dir)) { Beam_stem_segment const &neighbor_seg = segs[j + event_dir]; // TODO: make names clearer? --jneem @@ -465,8 +461,11 @@ Beam::get_beam_segments (Grob *me_grob, Grob **common) || abs (vertical_count) >= neighbor_seg.max_connect_); if (!event) - // Then this edge of the current segment is irrelevent because it will + // Then this edge of the current segment is irrelevant because it will // be connected with the next segment in the event_dir direction. + // If we skip the left edge here, the right edge of + // the previous segment has already been skipped since + // the conditions are symmetric continue; current.vertical_count_ = vertical_count; @@ -479,8 +478,8 @@ Beam::get_beam_segments (Grob *me_grob, Grob **common) && me->get_bound (event_dir)->break_status_dir ()) { current.horizontal_[event_dir] - = (robust_relative_extent (me->get_bound (event_dir), - commonx, X_AXIS)[RIGHT] + = (Axis_group_interface::generic_bound_extent (me->get_bound (event_dir), + commonx, X_AXIS)[RIGHT] + event_dir * break_overshoot[event_dir]); } else @@ -537,11 +536,64 @@ Beam::get_beam_segments (Grob *me_grob, Grob **common) current = Beam_segment (); } } - while (flip (&event_dir) != LEFT); } } + SCM segments_scm = SCM_EOL; + SCM *tail = &segments_scm; + + for (vsize i = 0; i < segments.size (); i++) + { + *tail = scm_cons (scm_list_2 (scm_cons (ly_symbol2scm ("vertical-count"), + scm_from_int (segments[i].vertical_count_)), + scm_cons (ly_symbol2scm ("horizontal"), + ly_interval2scm (segments[i].horizontal_))), + SCM_EOL); + tail = SCM_CDRLOC (*tail); + } + + return segments_scm; +} + +MAKE_SCHEME_CALLBACK (Beam, calc_x_positions, 1); +SCM +Beam::calc_x_positions (SCM smob) +{ + Spanner *me = unsmob_spanner (smob); + SCM segments = me->get_property ("beam-segments"); + Interval x_positions; + x_positions.set_empty (); + for (SCM s = segments; scm_is_pair (s); s = scm_cdr (s)) + x_positions.unite (robust_scm2interval (ly_assoc_get (ly_symbol2scm ("horizontal"), + scm_car (s), + SCM_EOL), + Interval (0.0, 0.0))); + + // Case for beams without segments (i.e. uniting two skips with a beam) + // TODO: should issue a warning? warning likely issued downstream, but couldn't hurt... + if (x_positions.is_empty ()) + { + extract_grob_set (me, "stems", stems); + Grob *common_x = common_refpoint_of_array (stems, me, X_AXIS); + for (LEFT_and_RIGHT (d)) + x_positions[d] = me->relative_coordinate (common_x, X_AXIS); + } + return ly_interval2scm (x_positions); +} + +vector +Beam::get_beam_segments (Grob *me) +{ + SCM segments_scm = me->get_property ("beam-segments"); + vector segments; + for (SCM s = segments_scm; scm_is_pair (s); s = scm_cdr (s)) + { + segments.push_back (Beam_segment ()); + segments.back ().vertical_count_ = robust_scm2int (ly_assoc_get (ly_symbol2scm ("vertical-count"), scm_car (s), SCM_EOL), 0); + segments.back ().horizontal_ = robust_scm2interval (ly_assoc_get (ly_symbol2scm ("horizontal"), scm_car (s), SCM_EOL), Interval (0.0, 0.0)); + } + return segments; } @@ -550,27 +602,28 @@ SCM Beam::print (SCM grob) { Spanner *me = unsmob_spanner (grob); - Grob *commonx = 0; - vector segments = get_beam_segments (me, &commonx); + /* + TODO - mild code dup for all the commonx calls. + Some use just common_refpoint_of_array, some (in print and + calc_beam_segments) use this plus calls to get_bound. + + Figure out if there is any particular reason for this and + consolidate in one Beam::get_common function. + */ + extract_grob_set (me, "stems", stems); + Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS); + for (LEFT_and_RIGHT (d)) + commonx = me->get_bound (d)->common_refpoint (commonx, X_AXIS); + + vector segments = get_beam_segments (me); + if (!segments.size ()) return SCM_EOL; - Interval span; - if (normal_stem_count (me)) - { - span[LEFT] = first_normal_stem (me)->relative_coordinate (commonx, X_AXIS); - span[RIGHT] = last_normal_stem (me)->relative_coordinate (commonx, X_AXIS); - } - else - { - extract_grob_set (me, "stems", stems); - span[LEFT] = stems[0]->relative_coordinate (commonx, X_AXIS); - span[RIGHT] = stems.back ()->relative_coordinate (commonx, X_AXIS); - } - Real blot = me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter")); SCM posns = me->get_property ("quantized-positions"); + Interval span = robust_scm2interval (me->get_property ("X-positions"), Interval (0, 0)); Interval pos; if (!is_number_pair (posns)) { @@ -593,7 +646,6 @@ Beam::print (SCM grob) Interval placements = robust_scm2interval (me->get_property ("normalized-endpoints"), Interval (0.0, 0.0)); Stencil the_beam; - int extreme = (segments[0].vertical_count_ == 0 ? segments[0].vertical_count_ : segments.back ().vertical_count_); @@ -701,13 +753,11 @@ Beam::get_default_dir (Grob *me) for (iterof (s, stems); s != stems.end (); s++) { Interval positions = Stem::head_positions (*s); - Direction d = DOWN; - do + for (DOWN_and_UP (d)) { if (sign (positions[d]) == d) extremes[d] = d * max (d * positions[d], d * extremes[d]); } - while (flip (&d) != DOWN); } Drul_array total (0, 0); @@ -918,15 +968,17 @@ Beam::calc_stem_shorten (SCM smob) return scm_from_double (0.0); } -MAKE_SCHEME_CALLBACK (Beam, quanting, 1); +MAKE_SCHEME_CALLBACK (Beam, quanting, 3); SCM -Beam::quanting (SCM smob) +Beam::quanting (SCM smob, SCM ys_scm, SCM align_broken_intos) { Grob *me = unsmob_grob (smob); - Drul_array ys (0, 0); - Beam_scoring_problem problem (me, ys); + Drul_array ys = robust_scm2drul (ys_scm, Drul_array (infinity_f, -infinity_f)); + bool cbs = to_boolean (align_broken_intos); + Beam_scoring_problem problem (me, ys, cbs); ys = problem.solve (); + return ly_interval2scm (ys); } @@ -1033,8 +1085,7 @@ Beam::set_stem_lengths (SCM smob) Grob *fvs = first_normal_stem (me); Grob *lvs = last_normal_stem (me); - Real xl = fvs ? fvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0; - Real xr = lvs ? lvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0; + Interval x_span = robust_scm2interval (me->get_property ("X-positions"), Interval (0, 0)); Direction feather_dir = to_dir (me->get_property ("grow-direction")); for (vsize i = 0; i < stems.size (); i++) @@ -1043,7 +1094,7 @@ Beam::set_stem_lengths (SCM smob) bool french = to_boolean (s->get_property ("french-beaming")); Real stem_y = calc_stem_y (me, s, common, - xl, xr, feather_dir, + x_span[LEFT], x_span[RIGHT], feather_dir, pos, french && s != lvs && s != fvs); /* @@ -1069,13 +1120,12 @@ Beam::set_beaming (Grob *me, Beaming_pattern const *beaming) { extract_grob_set (me, "stems", stems); - Direction d = LEFT; for (vsize i = 0; i < stems.size (); i++) { /* Don't overwrite user settings. */ - do + for (LEFT_and_RIGHT (d)) { Grob *stem = stems[i]; SCM beaming_prop = stem->get_property ("beaming"); @@ -1097,7 +1147,6 @@ Beam::set_beaming (Grob *me, Beaming_pattern const *beaming) Stem::set_beaming (stem, count, d); } } - while (flip (&d) != LEFT); } } @@ -1174,23 +1223,27 @@ Beam::rest_collision_callback (SCM smob, SCM prev_offset) || !Beam::normal_stem_count (beam)) return scm_from_double (0.0); + Grob *common_y = rest->common_refpoint (beam, Y_AXIS); + Drul_array pos (robust_scm2drul (beam->get_property ("positions"), Drul_array (0, 0))); + for (LEFT_and_RIGHT (dir)) + pos[dir] += beam->relative_coordinate (common_y, Y_AXIS); + Real staff_space = Staff_symbol_referencer::staff_space (rest); scale_drul (&pos, staff_space); Real dy = pos[RIGHT] - pos[LEFT]; - Drul_array visible_stems (first_normal_stem (beam), - last_normal_stem (beam)); extract_grob_set (beam, "stems", stems); - Grob *common = common_refpoint_of_array (stems, beam, X_AXIS); - Real x0 = visible_stems[LEFT]->relative_coordinate (common, X_AXIS); - Real dx = visible_stems[RIGHT]->relative_coordinate (common, X_AXIS) - x0; + Interval x_span = robust_scm2interval (beam->get_property ("X-positions"), + Interval (0.0, 0.0)); + Real x0 = x_span[LEFT]; + Real dx = x_span.length (); Real slope = dy && dx ? dy / dx : 0; Direction d = get_grob_direction (stem); @@ -1210,8 +1263,6 @@ Beam::rest_collision_callback (SCM smob, SCM prev_offset) + (beam_count - 1) * beam_translation; Real beam_y = stem_y - d * height_of_my_beams; - Grob *common_y = rest->common_refpoint (beam, Y_AXIS); - Interval rest_extent = rest->extent (rest, Y_AXIS); rest_extent.translate (offset + rest->get_parent (Y_AXIS)->relative_coordinate (common_y, Y_AXIS)); @@ -1238,35 +1289,39 @@ Beam::rest_collision_callback (SCM smob, SCM prev_offset) return scm_from_double (offset + staff_space * shift); } +/* + Estimate the position of a rest under a beam, + as the average position of its neighboring heads. +*/ MAKE_SCHEME_CALLBACK_WITH_OPTARGS (Beam, pure_rest_collision_callback, 4, 1, ""); SCM Beam::pure_rest_collision_callback (SCM smob, - SCM, /* prev_offset */ SCM, /* start */ - SCM /* end */) + SCM, /* end */ + SCM prev_offset) { - Real amount = 0.0; + Real previous = robust_scm2double (prev_offset, 0.0); Grob *me = unsmob_grob (smob); Grob *stem = unsmob_grob (me->get_object ("stem")); if (!stem) - return scm_from_double (amount); + return scm_from_double (previous); Grob *beam = unsmob_grob (stem->get_object ("beam")); if (!beam - || !Beam::normal_stem_count (beam)) - return scm_from_double (amount); + || !Beam::normal_stem_count (beam) + || !is_direction (beam->get_property_data ("direction"))) + return scm_from_double (previous); Real ss = Staff_symbol_referencer::staff_space (me); /* This gives the extrema of rest positions. - In general, beams are never typeset more than one staff space away - from the staff in either direction. + Even with noteheads on ledgers, beams typically remain within the staff, + and push rests at most one staff-space (2 positions) from the staff. */ Grob *staff = Staff_symbol_referencer::get_staff_symbol (me); Interval rest_max_pos = staff ? Staff_symbol::line_span (staff) : Interval (0.0, 0.0); - rest_max_pos.widen (1); - rest_max_pos *= ss / 2; + rest_max_pos.widen (2); extract_grob_set (beam, "stems", stems); vector my_stems; @@ -1286,8 +1341,8 @@ Beam::pure_rest_collision_callback (SCM smob, Grob *left; Grob *right; - if (idx == (vsize)-1 || my_stems.size () == 1) - return scm_from_double (amount); + if (idx == (vsize) - 1 || my_stems.size () == 1) + return scm_from_double (previous); else if (idx == 0) left = right = my_stems[1]; else if (idx == my_stems.size () - 1) @@ -1297,18 +1352,21 @@ Beam::pure_rest_collision_callback (SCM smob, left = my_stems[idx - 1]; right = my_stems[idx + 1]; } - Direction beamdir = get_grob_direction (beam); - /* - Take the position between the two bounding head_positions, - then bound it by the minimum and maximum positions outside the staff. - 4.0 = 2.0 to get out of staff space * 2.0 for the average - */ - amount = min (max ((Stem::head_positions (left)[beamdir] + Stem::head_positions (right)[beamdir]) / 4.0, rest_max_pos[DOWN]), rest_max_pos[UP]); - return scm_from_double (amount); + /* In stems with several heads, use the one closest to the beam. */ + Direction beamdir = get_grob_direction (beam); + Real shift = min (max ( (Stem::head_positions (left)[beamdir] + + Stem::head_positions (right)[beamdir]) / 2.0, + rest_max_pos[DOWN]), + rest_max_pos[UP] + ) * ss / 2.0 + - previous; + /* Always move by a whole number of staff spaces */ + shift = ceil (fabs (shift / ss)) * ss * sign (shift); + + return scm_from_double (previous + shift); } - bool Beam::is_knee (Grob *me) { @@ -1378,7 +1436,6 @@ ADD_INTERFACE (Beam, " measured in staffspace. The @code{direction} property is" " not user-serviceable. Use the @code{direction} property" " of @code{Stem} instead.\n" - "\n" "The following properties may be set in the @code{details}" " list.\n" "\n" @@ -1418,11 +1475,11 @@ ADD_INTERFACE (Beam, "auto-knee-gap " "beamed-stem-shorten " "beaming " + "beam-segments " "beam-thickness " "break-overshoot " "clip-edges " "concaveness " - "consistent-broken-slope " "collision-interfaces " "collision-voice-only " "covered-grobs " @@ -1443,4 +1500,5 @@ ADD_INTERFACE (Beam, "shorten " "skip-quanting " "stems " + "X-positions " );