X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=lily%2Falign-interface.cc;h=8a2a8d9b819a4469f2b60c73f75f1d0899963d91;hb=873151f815f8b8e27eb5231c74e28392561e4668;hp=aee6dad7058411b187c9b7388cca059e3d92aec1;hpb=94345c6367d9e6a7dac6711cbb81845e8e889db5;p=lilypond.git diff --git a/lily/align-interface.cc b/lily/align-interface.cc index aee6dad705..8a2a8d9b81 100644 --- a/lily/align-interface.cc +++ b/lily/align-interface.cc @@ -61,73 +61,85 @@ Align_interface::align_to_ideal_distances (SCM smob) return SCM_BOOL_T; } -/* Return upper and lower skylines for VerticalAxisGroup g. If the extent - is non-empty but there is no skyline available (or pure is true), just +/* for each grob, find its upper and lower skylines. If the grob has + an empty extent, delete it from the list instead. If the extent is + non-empty but there is no skyline available (or pure is true), just create a flat skyline from the bounding box */ // TODO(jneem): the pure and non-pure parts seem to share very little // code. Split them into 2 functions, perhaps? -static Skyline_pair -get_skylines (Grob *g, +static void +get_skylines (Grob *me, + vector *const elements, Axis a, - Grob *other_common, - bool pure, int start, int end) + bool pure, int start, int end, + vector *const ret) { - Skyline_pair skylines; + Grob *other_common = common_refpoint_of_array (*elements, me, other_axis (a)); - if (!pure) + for (vsize i = elements->size (); i--;) { - Skyline_pair *skys = Skyline_pair::unsmob (g->get_property (a == Y_AXIS - ? "vertical-skylines" - : "horizontal-skylines")); - if (skys) - skylines = *skys; - - /* This skyline was calculated relative to the grob g. In order to compare it to - skylines belonging to other grobs, we need to shift it so that it is relative - to the common reference. */ - Real offset = g->relative_coordinate (other_common, other_axis (a)); - skylines.shift (offset); - } - else if (Hara_kiri_group_spanner::request_suicide (g, start, end)) - return skylines; - else - { - assert (a == Y_AXIS); - Interval extent = g->pure_height (g, start, end); - - // This is a hack to get better accuracy on the pure-height of VerticalAlignment. - // It's quite common for a treble clef to be the highest element of one system - // and for a low note (or lyrics) to be the lowest note on another. The two will - // never collide, but the pure-height stuff only works with bounding boxes, so it - // doesn't know that. The result is a significant over-estimation of the pure-height, - // especially on systems with many staves. To correct for this, we build a skyline - // in two parts: the part we did above contains most of the grobs (note-heads, etc.) - // while the bit we're about to do only contains the breakable grobs at the beginning - // of the system. This way, the tall treble clefs are only compared with the treble - // clefs of the other staff and they will be ignored if the staff above is, for example, - // lyrics. - if (Axis_group_interface::has_interface (g)) + Grob *g = (*elements)[i]; + Skyline_pair skylines; + + if (!pure) { - extent = Axis_group_interface::rest_of_line_pure_height (g, start, end); - Interval begin_of_line_extent = Axis_group_interface::begin_of_line_pure_height (g, start); - if (!begin_of_line_extent.is_empty ()) + Skyline_pair *skys = Skyline_pair::unsmob (g->get_property (a == Y_AXIS + ? "vertical-skylines" + : "horizontal-skylines")); + if (skys) + skylines = *skys; + + /* This skyline was calculated relative to the grob g. In order to compare it to + skylines belonging to other grobs, we need to shift it so that it is relative + to the common reference. */ + Real offset = g->relative_coordinate (other_common, other_axis (a)); + skylines.shift (offset); + } + else + { + assert (a == Y_AXIS); + Interval extent = g->pure_height (g, start, end); + + // This is a hack to get better accuracy on the pure-height of VerticalAlignment. + // It's quite common for a treble clef to be the highest element of one system + // and for a low note (or lyrics) to be the lowest note on another. The two will + // never collide, but the pure-height stuff only works with bounding boxes, so it + // doesn't know that. The result is a significant over-estimation of the pure-height, + // especially on systems with many staves. To correct for this, we build a skyline + // in two parts: the part we did above contains most of the grobs (note-heads, etc.) + // while the bit we're about to do only contains the breakable grobs at the beginning + // of the system. This way, the tall treble clefs are only compared with the treble + // clefs of the other staff and they will be ignored if the staff above is, for example, + // lyrics. + if (Axis_group_interface::has_interface (g) + && !Hara_kiri_group_spanner::request_suicide (g, start, end)) + { + extent = Axis_group_interface::rest_of_line_pure_height (g, start, end); + Interval begin_of_line_extent = Axis_group_interface::begin_of_line_pure_height (g, start); + if (!begin_of_line_extent.is_empty ()) + { + Box b; + b[a] = begin_of_line_extent; + b[other_axis (a)] = Interval (-infinity_f, -1); + skylines.insert (b, other_axis (a)); + } + } + + if (!extent.is_empty ()) { Box b; - b[a] = begin_of_line_extent; - b[other_axis (a)] = Interval (-infinity_f, -1); + b[a] = extent; + b[other_axis (a)] = Interval (0, infinity_f); skylines.insert (b, other_axis (a)); } } - if (!extent.is_empty ()) - { - Box b; - b[a] = extent; - b[other_axis (a)] = Interval (0, infinity_f); - skylines.insert (b, other_axis (a)); - } + if (skylines.is_empty ()) + elements->erase (elements->begin () + i); + else + ret->push_back (skylines); } - return skylines; + reverse (*ret); } vector @@ -165,7 +177,7 @@ Align_interface::get_minimum_translations_without_min_dist (Grob *me, // else centered dynamics will break when there is a fixed alignment). vector Align_interface::internal_get_minimum_translations (Grob *me, - vector const &elems, + vector const &all_grobs, Axis a, bool include_fixed_spacing, bool pure, int start, int end) @@ -192,14 +204,15 @@ Align_interface::internal_get_minimum_translations (Grob *me, Direction stacking_dir = robust_scm2dir (me->get_property ("stacking-dir"), DOWN); + vector elems (all_grobs); // writable copy + vector skylines; - Grob *other_common = common_refpoint_of_array (elems, me, other_axis (a)); + get_skylines (me, &elems, a, pure, start, end, &skylines); Real where = 0; Real default_padding = robust_scm2double (me->get_property ("padding"), 0.0); vector translates; Skyline down_skyline (stacking_dir); - Grob *last_nonempty_element = 0; Real last_spaceable_element_pos = 0; Grob *last_spaceable_element = 0; Skyline last_spaceable_skyline (stacking_dir); @@ -209,18 +222,14 @@ Align_interface::internal_get_minimum_translations (Grob *me, Real dy = 0; Real padding = default_padding; - Skyline_pair skyline = get_skylines (elems[j], a, other_common, pure, start, end); - - if (skyline.is_empty ()) - dy = 0.0; - else if (!last_nonempty_element) - dy = skyline[-stacking_dir].max_height () + padding; + if (j == 0) + dy = skylines[j][-stacking_dir].max_height () + padding; else { - SCM spec = Page_layout_problem::get_spacing_spec (last_nonempty_element, elems[j], pure, start, end); + SCM spec = Page_layout_problem::get_spacing_spec (elems[j - 1], elems[j], pure, start, end); Page_layout_problem::read_spacing_spec (spec, &padding, ly_symbol2scm ("padding")); - dy = down_skyline.distance (skyline[-stacking_dir]) + padding; + dy = down_skyline.distance (skylines[j][-stacking_dir]) + padding; Real spec_distance = 0; if (Page_layout_problem::read_spacing_spec (spec, &spec_distance, ly_symbol2scm ("minimum-distance"))) @@ -240,7 +249,7 @@ Align_interface::internal_get_minimum_translations (Grob *me, Page_layout_problem::read_spacing_spec (spec, &spaceable_padding, ly_symbol2scm ("padding")); - dy = max (dy, (last_spaceable_skyline.distance (skyline[-stacking_dir]) + dy = max (dy, (last_spaceable_skyline.distance (skylines[j][-stacking_dir]) + stacking_dir * (last_spaceable_element_pos - where) + spaceable_padding)); Real spaceable_min_distance = 0; @@ -254,9 +263,12 @@ Align_interface::internal_get_minimum_translations (Grob *me, } } + if (isinf (dy)) /* if the skyline is empty, maybe max_height is infinity_f */ + dy = 0.0; + dy = max (0.0, dy); down_skyline.raise (-stacking_dir * dy); - down_skyline.merge (skyline[stacking_dir]); + down_skyline.merge (skylines[j][stacking_dir]); where += stacking_dir * dy; translates.push_back (where); @@ -267,19 +279,32 @@ Align_interface::internal_get_minimum_translations (Grob *me, last_spaceable_element_pos = where; last_spaceable_skyline = down_skyline; } - if (!skyline.is_empty ()) - last_nonempty_element = elems[j]; + } + + // So far, we've computed the translates for all the non-empty elements. + // Here, we set the translates for the empty elements: an empty element + // gets the same translation as the last non-empty element before it. + vector all_translates; + if (!translates.empty ()) + { + Real w = translates[0]; + for (vsize i = 0, j = 0; j < all_grobs.size (); j++) + { + if (i < elems.size () && all_grobs[j] == elems[i]) + w = translates[i++]; + all_translates.push_back (w); + } } if (pure) { SCM mta = me->get_property ("minimum-translations-alist"); mta = scm_cons (scm_cons (scm_cons (scm_from_int (start), scm_from_int (end)), - ly_floatvector2scm (translates)), + ly_floatvector2scm (all_translates)), mta); me->set_property ("minimum-translations-alist", mta); } - return translates; + return all_translates; } void