X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=lily%2Fbeam.cc;h=a50f2904bc0b00e4a497c16c908b13cabf38d96b;hb=5d84bfad4626892bcffd05adcced53c8a2329047;hp=1ecd71a88ff5a87027c7aa987c67292a5954196d;hpb=b9a18c38bf25fe66b0ecea5e83d7beaf69ffa6c0;p=lilypond.git diff --git a/lily/beam.cc b/lily/beam.cc index 1ecd71a88f..a50f2904bc 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--2015 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" @@ -45,7 +46,6 @@ #include "international.hh" #include "interval-set.hh" #include "item.hh" -#include "least-squares.hh" #include "lookup.hh" #include "main.hh" #include "misc.hh" @@ -119,11 +119,13 @@ Beam::get_beam_translation (Grob *me) Real beam_thickness = get_beam_thickness (me); Real fract = robust_scm2double (me->get_property ("length-fraction"), 1.0); - Real beam_translation = beam_count < 4 - ? (2 * staff_space + line - beam_thickness) / 2.0 - : (3 * staff_space + line - beam_thickness) / 3.0; - - return fract * beam_translation; + /* + if fract != 1.0, as is the case for grace notes, we want the gap + to decrease too. To achieve this, we divide the thickness by + fract */ + return (beam_count < 4 + ? (2 * staff_space * fract + line * fract - beam_thickness) / 2.0 + : (3 * staff_space * fract + line * fract - beam_thickness) / 3.0); } /* Maximum beam_count. */ @@ -145,11 +147,11 @@ MAKE_SCHEME_CALLBACK (Beam, calc_normal_stems, 1); SCM Beam::calc_normal_stems (SCM smob) { - Grob *me = unsmob_grob (smob); + Grob *me = unsmob (smob); extract_grob_set (me, "stems", stems); SCM val = Grob_array::make_array (); - Grob_array *ga = unsmob_grob_array (val); + Grob_array *ga = unsmob (val); for (vsize i = 0; i < stems.size (); i++) if (Stem::is_normal_stem (stems[i])) ga->add (stems[i]); @@ -161,7 +163,7 @@ MAKE_SCHEME_CALLBACK (Beam, calc_direction, 1); SCM Beam::calc_direction (SCM smob) { - Grob *me = unsmob_grob (smob); + Grob *me = unsmob (smob); /* Beams with less than 2 two stems don't make much sense, but could happen when you do @@ -197,6 +199,19 @@ Beam::calc_direction (SCM smob) dir = to_dir (stem->get_property_data ("direction")); else dir = to_dir (stem->get_property ("default-direction")); + + extract_grob_set (stem, "note-heads", heads); + /* default position of Kievan heads with beams is down + placing this here avoids warnings downstream */ + if (heads.size()) + { + if (scm_is_eq (heads[0]->get_property ("style"), + ly_symbol2scm ("kievan"))) + { + if (dir == CENTER) + dir = DOWN; + } + } } } @@ -244,7 +259,7 @@ position_with_maximal_common_beams (SCM left_beaming, SCM right_beaming, for (SCM s = scm_car (right_beaming); scm_is_pair (s); s = scm_cdr (s)) { int k = -right_dir * scm_to_int (scm_car (s)) + i; - if (scm_c_memq (scm_from_int (k), left_beaming) != SCM_BOOL_F) + if (scm_is_true (ly_memv (scm_from_int (k), left_beaming))) count++; } @@ -262,7 +277,7 @@ MAKE_SCHEME_CALLBACK (Beam, calc_beaming, 1) SCM Beam::calc_beaming (SCM smob) { - Grob *me = unsmob_grob (smob); + Grob *me = unsmob (smob); extract_grob_set (me, "stems", stems); @@ -284,9 +299,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); @@ -299,7 +313,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; @@ -337,23 +350,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 (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); @@ -374,8 +386,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 @@ -414,7 +426,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 @@ -443,9 +454,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 @@ -466,8 +476,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; @@ -480,8 +493,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 @@ -538,11 +551,62 @@ Beam::get_beam_segments (Grob *me_grob, Grob **common) current = Beam_segment (); } } - while (flip (&event_dir) != LEFT); } } + SCM segments_scm = SCM_EOL; + + for (vsize i = segments.size (); i--;) + { + segments_scm = 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_))), + segments_scm); + } + + return segments_scm; +} + +MAKE_SCHEME_CALLBACK (Beam, calc_x_positions, 1); +SCM +Beam::calc_x_positions (SCM smob) +{ + Spanner *me = unsmob (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,28 +614,29 @@ MAKE_SCHEME_CALLBACK (Beam, print, 1); SCM Beam::print (SCM grob) { - Spanner *me = unsmob_spanner (grob); - Grob *commonx = 0; - vector segments = get_beam_segments (me, &commonx); + Spanner *me = unsmob (grob); + /* + 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)) { @@ -594,10 +659,9 @@ 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_); + vsize extreme = (segments[0].vertical_count_ == 0 + ? segments[0].vertical_count_ + : segments.back ().vertical_count_); for (vsize i = 0; i < segments.size (); i++) { @@ -624,7 +688,7 @@ Beam::print (SCM grob) // we need two translations: the normal one and // the one of the lowest segment - int idx[] = {i, extreme}; + size_t idx[] = {i, extreme}; Real translations[2]; for (int j = 0; j < 2; j++) @@ -678,7 +742,7 @@ Beam::print (SCM grob) Direction stem_dir = stems.size () ? to_dir (stems[0]->get_property ("direction")) : UP; - Stencil score = *unsmob_stencil (Text_interface::interpret_markup + Stencil score = *unsmob (Text_interface::interpret_markup (me->layout ()->self_scm (), properties, annotation)); if (!score.is_empty ()) @@ -702,13 +766,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); @@ -799,9 +861,7 @@ Beam::consider_auto_knees (Grob *me) if (!scm_is_number (scm)) return; - Interval_set gaps; - - gaps.set_full (); + vector forbidden_intervals; extract_grob_set (me, "normal-stems", stems); @@ -813,11 +873,11 @@ Beam::consider_auto_knees (Grob *me) { Grob *stem = stems[i]; - Interval head_extents = Stem::head_positions (stem); - if (!head_extents.is_empty ()) + Interval head_extents; + if (Stem::head_count (stem)) { - head_extents[LEFT] += -1; - head_extents[RIGHT] += 1; + head_extents = Stem::head_positions (stem); + head_extents.widen (1); head_extents *= staff_space * 0.5; /* @@ -835,15 +895,17 @@ Beam::consider_auto_knees (Grob *me) } head_extents_array.push_back (head_extents); - gaps.remove_interval (head_extents); + forbidden_intervals.push_back (head_extents); } Interval max_gap; Real max_gap_len = 0.0; - for (vsize i = gaps.allowed_regions_.size () - 1; i != VPOS; i--) + vector allowed_regions + = Interval_set::interval_union (forbidden_intervals).complement ().intervals (); + for (vsize i = allowed_regions.size () - 1; i != VPOS; i--) { - Interval gap = gaps.allowed_regions_[i]; + Interval gap = allowed_regions[i]; /* the outer gaps are not knees. @@ -884,43 +946,11 @@ Beam::consider_auto_knees (Grob *me) } } -/* Set stem's shorten property if unset. - -TODO: -take some y-position (chord/beam/nearest?) into account -scmify forced-fraction - -This is done in beam because the shorten has to be uniform over the -entire beam. -*/ - -void -set_minimum_dy (Grob *me, Real *dy) -{ - if (*dy) - { - /* - If dy is smaller than the smallest quant, we - get absurd direction-sign penalties. - */ - - Real ss = Staff_symbol_referencer::staff_space (me); - Real beam_thickness = Beam::get_beam_thickness (me) / ss; - Real slt = Staff_symbol_referencer::line_thickness (me) / ss; - Real sit = (beam_thickness - slt) / 2; - Real inter = 0.5; - Real hang = 1.0 - (beam_thickness - slt) / 2; - - *dy = sign (*dy) * max (fabs (*dy), - min (min (sit, inter), hang)); - } -} - MAKE_SCHEME_CALLBACK (Beam, calc_stem_shorten, 1) SCM Beam::calc_stem_shorten (SCM smob) { - Grob *me = unsmob_grob (smob); + Grob *me = unsmob (smob); /* shortening looks silly for x staff beams @@ -934,7 +964,7 @@ Beam::calc_stem_shorten (SCM smob) int beam_count = get_beam_count (me); SCM shorten_list = me->get_property ("beamed-stem-shorten"); - if (shorten_list == SCM_EOL) + if (scm_is_null (shorten_list)) return scm_from_int (0); Real staff_space = Staff_symbol_referencer::staff_space (me); @@ -951,476 +981,17 @@ Beam::calc_stem_shorten (SCM smob) return scm_from_double (0.0); } -Interval -Beam::no_visible_stem_positions (Grob *me, Interval default_value) -{ - extract_grob_set (me, "stems", stems); - if (stems.empty ()) - return default_value; - - Interval head_positions; - Slice multiplicity; - for (vsize i = 0; i < stems.size (); i++) - { - head_positions.unite (Stem::head_positions (stems[i])); - multiplicity.unite (Stem::beam_multiplicity (stems[i])); - } - - Direction dir = get_grob_direction (me); - - if (!dir) - programming_error ("The beam should have a direction by now."); - - Real y = head_positions.linear_combination (dir) - * 0.5 * Staff_symbol_referencer::staff_space (me) - + dir * get_beam_translation (me) * (multiplicity.length () + 1); - - y /= Staff_symbol_referencer::staff_space (me); - return Interval (y, y); -} - -/* - Compute a first approximation to the beam slope. -*/ -MAKE_SCHEME_CALLBACK (Beam, calc_least_squares_positions, 2); -SCM -Beam::calc_least_squares_positions (SCM smob, SCM /* posns */) -{ - Grob *me = unsmob_grob (smob); - - int count = normal_stem_count (me); - Interval pos (0, 0); - if (count < 1) - return ly_interval2scm (no_visible_stem_positions (me, pos)); - - vector x_posns; - extract_grob_set (me, "normal-stems", stems); - Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS); - Grob *commony = common_refpoint_of_array (stems, me, Y_AXIS); - - Real my_y = me->relative_coordinate (commony, Y_AXIS); - - Grob *fvs = first_normal_stem (me); - Grob *lvs = last_normal_stem (me); - - Interval ideal (Stem::get_stem_info (fvs).ideal_y_ - + fvs->relative_coordinate (commony, Y_AXIS) - my_y, - Stem::get_stem_info (lvs).ideal_y_ - + lvs->relative_coordinate (commony, Y_AXIS) - my_y); - - Real x0 = first_normal_stem (me)->relative_coordinate (commonx, X_AXIS); - for (vsize i = 0; i < stems.size (); i++) - { - Grob *s = stems[i]; - - Real x = s->relative_coordinate (commonx, X_AXIS) - x0; - x_posns.push_back (x); - } - Real dx = last_normal_stem (me)->relative_coordinate (commonx, X_AXIS) - x0; - - Real y = 0; - Real slope = 0; - Real dy = 0; - Real ldy = 0.0; - if (!ideal.delta ()) - { - Interval chord (Stem::chord_start_y (stems[0]), - Stem::chord_start_y (stems.back ())); - - /* Simple beams (2 stems) on middle line should be allowed to be - slightly sloped. - - However, if both stems reach middle line, - ideal[LEFT] == ideal[RIGHT] and ideal.delta () == 0. - - For that case, we apply artificial slope */ - if (!ideal[LEFT] && chord.delta () && count == 2) - { - /* FIXME. -> UP */ - Direction d = (Direction) (sign (chord.delta ()) * UP); - pos[d] = get_beam_thickness (me) / 2; - pos[-d] = -pos[d]; - } - else - pos = ideal; - - /* - For broken beams this doesn't work well. In this case, the - slope esp. of the first part of a broken beam should predict - where the second part goes. - */ - ldy = pos[RIGHT] - pos[LEFT]; - } - else - { - vector ideals; - for (vsize i = 0; i < stems.size (); i++) - { - Grob *s = stems[i]; - ideals.push_back (Offset (x_posns[i], - Stem::get_stem_info (s).ideal_y_ - + s->relative_coordinate (commony, Y_AXIS) - - my_y)); - } - - minimise_least_squares (&slope, &y, ideals); - - dy = slope * dx; - - set_minimum_dy (me, &dy); - - ldy = dy; - pos = Interval (y, (y + dy)); - } - - /* - "position" is relative to the staff. - */ - scale_drul (&pos, 1 / Staff_symbol_referencer::staff_space (me)); - - me->set_property ("least-squares-dy", scm_from_double (ldy)); - return ly_interval2scm (pos); -} - -// Assuming V is not empty, pick a 'reasonable' point inside V. -static Real -point_in_interval (Interval v, Real dist) -{ - if (isinf (v[DOWN])) - return v[UP] - dist; - else if (isinf (v[UP])) - return v[DOWN] + dist; - else - return v.center (); -} - -/* - We can't combine with previous function, since check concave and - slope damping comes first. - - TODO: we should use the concaveness to control the amount of damping - applied. -*/ -MAKE_SCHEME_CALLBACK (Beam, shift_region_to_valid, 2); -SCM -Beam::shift_region_to_valid (SCM grob, SCM posns) -{ - Grob *me = unsmob_grob (grob); - - /* - Code dup. - */ - vector x_posns; - extract_grob_set (me, "stems", stems); - extract_grob_set (me, "covered-grobs", covered); - - Grob *common[NO_AXES] = { me, me }; - for (Axis a = X_AXIS; a < NO_AXES; incr (a)) - { - common[a] = common_refpoint_of_array (stems, me, a); - common[a] = common_refpoint_of_array (covered, common[a], a); - } - Grob *fvs = first_normal_stem (me); - - if (!fvs) - return posns; - Interval x_span; - x_span[LEFT] = fvs->relative_coordinate (common[X_AXIS], X_AXIS); - for (vsize i = 0; i < stems.size (); i++) - { - Grob *s = stems[i]; - - Real x = s->relative_coordinate (common[X_AXIS], X_AXIS) - x_span[LEFT]; - x_posns.push_back (x); - } - - Grob *lvs = last_normal_stem (me); - x_span[RIGHT] = lvs->relative_coordinate (common[X_AXIS], X_AXIS); - - Drul_array pos = ly_scm2interval (posns); - - scale_drul (&pos, Staff_symbol_referencer::staff_space (me)); - - Real beam_dy = pos[RIGHT] - pos[LEFT]; - Real beam_left_y = pos[LEFT]; - Real slope = x_span.delta () ? (beam_dy / x_span.delta ()) : 0.0; - - /* - Shift the positions so that we have a chance of finding good - quants (i.e. no short stem failures.) - */ - Interval feasible_left_point; - feasible_left_point.set_full (); - - for (vsize i = 0; i < stems.size (); i++) - { - Grob *s = stems[i]; - if (Stem::is_invisible (s)) - continue; - - Direction d = get_grob_direction (s); - Real left_y - = Stem::get_stem_info (s).shortest_y_ - - slope * x_posns [i]; - - /* - left_y is now relative to the stem S. We want relative to - ourselves, so translate: - */ - left_y - += + s->relative_coordinate (common[Y_AXIS], Y_AXIS) - - me->relative_coordinate (common[Y_AXIS], Y_AXIS); - - Interval flp; - flp.set_full (); - flp[-d] = left_y; - - feasible_left_point.intersect (flp); - } - - vector filtered; - /* - We only update these for objects that are too large for quanting - to find a workaround. Typically, these are notes with - stems, and timesig/keysig/clef, which take out the entire area - inside the staff as feasible. - - The code below disregards the thickness and multiplicity of the - beam. This should not be a problem, as the beam quanting will - take care of computing the impact those exactly. - */ - Real min_y_size = 2.0; - - // A list of intervals into which beams may not fall - vector forbidden_intervals; - - for (vsize i = 0; i < covered.size (); i++) - { - if (!covered[i]->is_live ()) - continue; - - if (Beam::has_interface (covered[i]) && is_cross_staff (covered[i])) - continue; - - Box b; - for (Axis a = X_AXIS; a < NO_AXES; incr (a)) - b[a] = covered[i]->extent (common[a], a); - - if (b[X_AXIS].is_empty () || b[Y_AXIS].is_empty ()) - continue; - - if (intersection (b[X_AXIS], x_span).is_empty ()) - continue; - - filtered.push_back (covered[i]); - Grob *head_stem = Rhythmic_head::get_stem (covered[i]); - if (head_stem && Stem::is_normal_stem (head_stem) - && Note_head::has_interface (covered[i])) - { - if (Stem::get_beam (head_stem)) - { - /* - We must assume that stems are infinitely long in this - case, as asking for the length of the stem typically - leads to circular dependencies. - - This strategy assumes that we don't want to handle the - collision of beams in opposite non-forced directions - with this code, where shortening the stems of both - would resolve the problem, eg. - - x x - | | - ===== - - ===== - | | - x x - - Such beams would need a coordinating grob to resolve - the collision, since both will likely want to occupy - the centerline. - */ - Direction stemdir = get_grob_direction (head_stem); - b[Y_AXIS][stemdir] = stemdir * infinity_f; - } - else - { - // TODO - should we include the extent of the stem here? - } - } - - if (b[Y_AXIS].length () < min_y_size) - continue; - - Direction d = LEFT; - do - { - Real x = b[X_AXIS][d] - x_span[LEFT]; - Real dy = slope * x; - - Direction yd = DOWN; - Interval disallowed; - do - { - Real left_y = b[Y_AXIS][yd]; - - left_y -= dy; - - // Translate back to beam as ref point. - left_y -= me->relative_coordinate (common[Y_AXIS], Y_AXIS); - - disallowed[yd] = left_y; - } - while (flip (&yd) != DOWN); - - forbidden_intervals.push_back (disallowed); - } - while (flip (&d) != LEFT); - } - - Grob_array *arr - = Pointer_group_interface::get_grob_array (me, - ly_symbol2scm ("covered-grobs")); - arr->set_array (filtered); - - vector_sort (forbidden_intervals, Interval::left_less); - Real epsilon = 1.0e-10; - Interval feasible_beam_placements (beam_left_y, beam_left_y); - - /* - forbidden_intervals contains a vector of intervals in which - the beam cannot start. it iterates through these intervals, - pushing feasible_beam_placements epsilon over or epsilon under a - collision. when this type of change happens, the loop is marked - as "dirty" and re-iterated. - - TODO: figure out a faster ways that this loop can happen via - a better search algorithm and/or OOP. - */ - - bool dirty = false; - do - { - dirty = false; - for (vsize i = 0; i < forbidden_intervals.size (); i++) - { - Direction d = DOWN; - do - { - if (forbidden_intervals[i][d] == d * infinity_f) - feasible_beam_placements[d] = d * infinity_f; - else if (forbidden_intervals[i].contains (feasible_beam_placements[d])) - { - feasible_beam_placements[d] = d * epsilon + forbidden_intervals[i][d]; - dirty = true; - } - } - while (flip (&d) != DOWN); - } - } - while (dirty); - - // if the beam placement falls out of the feasible region, we push it - // to infinity so that it can never be a feasible candidate below - Direction d = DOWN; - do - { - if (!feasible_left_point.contains (feasible_beam_placements[d])) - feasible_beam_placements[d] = d * infinity_f; - } - while (flip (&d) != DOWN); - - if ((feasible_beam_placements[UP] == infinity_f && feasible_beam_placements[DOWN] == -infinity_f) && !feasible_left_point.is_empty ()) - { - // We are somewhat screwed: we have a collision, but at least - // there is a way to satisfy stem length constraints. - beam_left_y = point_in_interval (feasible_left_point, 2.0); - } - else if (!feasible_left_point.is_empty ()) - { - // Only one of them offers is feasible solution. Pick that one. - if (abs (beam_left_y - feasible_beam_placements[DOWN]) > abs (beam_left_y - feasible_beam_placements[UP])) - beam_left_y = feasible_beam_placements[UP]; - else - beam_left_y = feasible_beam_placements[DOWN]; - } - else - { - // We are completely screwed. - me->warning (_ ("no viable initial configuration found: may not find good beam slope")); - } - - pos = Drul_array (beam_left_y, (beam_left_y + beam_dy)); - scale_drul (&pos, 1 / Staff_symbol_referencer::staff_space (me)); - - return ly_interval2scm (pos); -} - -/* This neat trick is by Werner Lemberg, - damped = tanh (slope) - corresponds with some tables in [Wanske] CHECKME */ -MAKE_SCHEME_CALLBACK (Beam, slope_damping, 2); -SCM -Beam::slope_damping (SCM smob, SCM posns) -{ - Grob *me = unsmob_grob (smob); - Drul_array pos = ly_scm2interval (posns); - - if (normal_stem_count (me) <= 1) - return posns; - - SCM s = me->get_property ("damping"); - Real damping = scm_to_double (s); - Real concaveness = robust_scm2double (me->get_property ("concaveness"), 0.0); - if (concaveness >= 10000) - { - pos[LEFT] = pos[RIGHT]; - me->set_property ("least-squares-dy", scm_from_double (0)); - damping = 0; - } - - if (damping) - { - scale_drul (&pos, Staff_symbol_referencer::staff_space (me)); - - Real dy = pos[RIGHT] - pos[LEFT]; - - Grob *fvs = first_normal_stem (me); - Grob *lvs = last_normal_stem (me); - - Grob *commonx = fvs->common_refpoint (lvs, X_AXIS); - - Real dx = last_normal_stem (me)->relative_coordinate (commonx, X_AXIS) - - first_normal_stem (me)->relative_coordinate (commonx, X_AXIS); - - Real slope = dy && dx ? dy / dx : 0; - - slope = 0.6 * tanh (slope) / (damping + concaveness); - - Real damped_dy = slope * dx; - - set_minimum_dy (me, &damped_dy); - - pos[LEFT] += (dy - damped_dy) / 2; - pos[RIGHT] -= (dy - damped_dy) / 2; - - scale_drul (&pos, 1 / Staff_symbol_referencer::staff_space (me)); - } - - return ly_interval2scm (pos); -} - -MAKE_SCHEME_CALLBACK (Beam, quanting, 2); +MAKE_SCHEME_CALLBACK (Beam, quanting, 3); SCM -Beam::quanting (SCM smob, SCM posns) +Beam::quanting (SCM smob, SCM ys_scm, SCM align_broken_intos) { - Grob *me = unsmob_grob (smob); - Drul_array ys (0, 0); - ys = robust_scm2drul (posns, ys); - Beam_scoring_problem problem (me, ys); + Grob *me = unsmob (smob); + 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); } @@ -1435,7 +1006,7 @@ where_are_the_whole_beams (SCM beaming) for (SCM s = scm_car (beaming); scm_is_pair (s); s = scm_cdr (s)) { - if (scm_c_memq (scm_car (s), scm_cdr (beaming)) != SCM_BOOL_F) + if (scm_is_true (ly_memv (scm_car (s), scm_cdr (beaming)))) l.add_point (scm_to_int (scm_car (s))); } @@ -1496,7 +1067,7 @@ MAKE_SCHEME_CALLBACK (Beam, set_stem_lengths, 1); SCM Beam::set_stem_lengths (SCM smob) { - Grob *me = unsmob_grob (smob); + Grob *me = unsmob (smob); /* trigger callbacks. */ (void) me->get_property ("direction"); @@ -1527,8 +1098,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++) @@ -1537,7 +1107,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); /* @@ -1563,18 +1133,17 @@ 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"); - if (beaming_prop == SCM_EOL - || index_get_cell (beaming_prop, d) == SCM_EOL) + if (scm_is_null (beaming_prop) + || scm_is_null (index_get_cell (beaming_prop, d))) { int count = beaming->beamlet_count (i, d); if (i > 0 @@ -1591,7 +1160,6 @@ Beam::set_beaming (Grob *me, Beaming_pattern const *beaming) Stem::set_beaming (stem, count, d); } } - while (flip (&d) != LEFT); } } @@ -1652,39 +1220,45 @@ MAKE_SCHEME_CALLBACK_WITH_OPTARGS (Beam, rest_collision_callback, 2, 1, ""); SCM Beam::rest_collision_callback (SCM smob, SCM prev_offset) { - Grob *rest = unsmob_grob (smob); + if (!scm_is_number (prev_offset)) + prev_offset = SCM_INUM0; + + Grob *rest = unsmob (smob); if (scm_is_number (rest->get_property ("staff-position"))) - return scm_from_int (0); + return prev_offset; - Real offset = robust_scm2double (prev_offset, 0.0); + Grob *stem = unsmob (rest->get_object ("stem")); - Grob *st = unsmob_grob (rest->get_object ("stem")); - Grob *stem = st; if (!stem) - return scm_from_double (0.0); - Grob *beam = unsmob_grob (stem->get_object ("beam")); + return prev_offset; + + Grob *beam = unsmob (stem->get_object ("beam")); if (!beam - || !Beam::has_interface (beam) + || !has_interface (beam) || !Beam::normal_stem_count (beam)) - return scm_from_double (0.0); + return prev_offset; + + 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); @@ -1704,10 +1278,9 @@ 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); - + Real offset = robust_scm2double (prev_offset, 0.0); Interval rest_extent = rest->extent (rest, Y_AXIS); - rest_extent.translate (offset + rest->get_parent (Y_AXIS)->relative_coordinate (common_y, Y_AXIS)); + rest_extent.translate (offset + rest->parent_relative (common_y, Y_AXIS)); Real rest_dim = rest_extent[d]; Real minimum_distance @@ -1717,51 +1290,47 @@ Beam::rest_collision_callback (SCM smob, SCM prev_offset) Real shift = d * min (d * (beam_y - d * minimum_distance - rest_dim), 0.0); shift /= staff_space; - Real rad = Staff_symbol_referencer::line_count (rest) * staff_space / 2; /* Always move discretely by half spaces */ shift = ceil (fabs (shift * 2.0)) / 2.0 * sign (shift); + Interval staff_span = Staff_symbol_referencer::staff_span (rest); + staff_span *= staff_space / 2; + /* Inside staff, move by whole spaces*/ - if ((rest_extent[d] + staff_space * shift) * d - < rad - || (rest_extent[-d] + staff_space * shift) * -d - < rad) + if (staff_span.contains (rest_extent[d] + staff_space * shift) + || staff_span.contains (rest_extent[-d] + staff_space * shift)) shift = ceil (fabs (shift)) * sign (shift); return scm_from_double (offset + staff_space * shift); } +/* + Estimate the position of a rest under a beam, + using 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; + if (!scm_is_number (prev_offset)) + prev_offset = SCM_INUM0; - Grob *me = unsmob_grob (smob); - Grob *stem = unsmob_grob (me->get_object ("stem")); + Grob *me = unsmob (smob); + Grob *stem = unsmob (me->get_object ("stem")); if (!stem) - return scm_from_double (amount); - Grob *beam = unsmob_grob (stem->get_object ("beam")); + return prev_offset; + Grob *beam = unsmob (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 prev_offset; 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. - */ - 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; - extract_grob_set (beam, "stems", stems); vector my_stems; @@ -1780,8 +1349,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 prev_offset; else if (idx == 0) left = right = my_stems[1]; else if (idx == my_stems.size () - 1) @@ -1791,18 +1360,30 @@ Beam::pure_rest_collision_callback (SCM smob, left = my_stems[idx - 1]; right = my_stems[idx + 1]; } + + /* Estimate the closest beam to be four positions away from the heads, */ 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]); + Real beam_pos = (Stem::head_positions (left)[beamdir] + + Stem::head_positions (right)[beamdir]) / 2.0 + + 4.0 * beamdir; // four staff-positions + /* and that the closest beam never crosses staff center by more than two positions */ + beam_pos = max (-2.0, beam_pos * beamdir) * beamdir; - return scm_from_double (amount); + Real minimum_distance + = ss * (robust_scm2double (stem->get_property ("stemlet-length"), 0.0) + + robust_scm2double (me->get_property ("minimum-distance"), 0.0)); + Real offset = beam_pos * ss / 2.0 + - minimum_distance * beamdir + - me->extent (me, Y_AXIS)[beamdir]; + Real previous = robust_scm2double (prev_offset, 0.0); + + /* Always move by a whole number of staff spaces, always away from the beam */ + offset = floor (min (0.0, (offset - previous) / ss * beamdir)) + * ss * beamdir + previous; + + return scm_from_double (offset); } - bool Beam::is_knee (Grob *me) { @@ -1844,7 +1425,7 @@ MAKE_SCHEME_CALLBACK (Beam, calc_cross_staff, 1) SCM Beam::calc_cross_staff (SCM smob) { - return scm_from_bool (is_cross_staff (unsmob_grob (smob))); + return scm_from_bool (is_cross_staff (unsmob (smob))); } int @@ -1872,7 +1453,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" @@ -1912,6 +1492,7 @@ ADD_INTERFACE (Beam, "auto-knee-gap " "beamed-stem-shorten " "beaming " + "beam-segments " "beam-thickness " "break-overshoot " "clip-edges " @@ -1934,5 +1515,7 @@ ADD_INTERFACE (Beam, "positions " "quantized-positions " "shorten " + "skip-quanting " "stems " + "X-positions " );