From: Joe Neeman Date: Fri, 23 Feb 2007 23:09:03 +0000 (+1100) Subject: Rework skyline algorithm for a more compact representation and less fuzzy X-Git-Tag: release/2.11.20-1~2^2~5^2~3 X-Git-Url: https://git.donarmstrong.com/?a=commitdiff_plain;h=1248062325ed281402c5d4fc63b1a67cb82cd791;p=lilypond.git Rework skyline algorithm for a more compact representation and less fuzzy comparisons. Include minimum-Y-extent boxes in vertical skylines. Make skyline debugging more verbose. Include arpeggios in separation-item skylines. --- diff --git a/lily/align-interface.cc b/lily/align-interface.cc index 6011a79c52..45761f40c8 100644 --- a/lily/align-interface.cc +++ b/lily/align-interface.cc @@ -123,7 +123,9 @@ get_skylines (Grob *me, to some absolute reference point */ if (!pure) { - Skyline_pair *skys = Skyline_pair::unsmob (g->get_property ("skylines")); + Skyline_pair *skys = Skyline_pair::unsmob (g->get_property (a == Y_AXIS + ? "vertical-skylines" + : "horizontal-skylines")); if (skys) skylines = *skys; diff --git a/lily/arpeggio.cc b/lily/arpeggio.cc index 234dbc44e3..56066912fb 100644 --- a/lily/arpeggio.cc +++ b/lily/arpeggio.cc @@ -147,6 +147,41 @@ Arpeggio::width (SCM smob) return ly_interval2scm (arpeggio.extent (X_AXIS)); } +MAKE_SCHEME_CALLBACK (Arpeggio, height, 1); +SCM +Arpeggio::height (SCM smob) +{ + return Grob::stencil_height (smob); +} + +MAKE_SCHEME_CALLBACK (Arpeggio, pure_height, 3); +SCM +Arpeggio::pure_height (SCM smob, SCM, SCM) +{ + Grob *me = unsmob_grob (smob); + if (to_boolean (me->get_property ("cross-staff"))) + return ly_interval2scm (Interval ()); + + return height (smob); +} + +MAKE_SCHEME_CALLBACK (Arpeggio, calc_cross_staff, 1); +SCM +Arpeggio::calc_cross_staff (SCM smob) +{ + Grob *me = unsmob_grob (smob); + extract_grob_set (me, "stems", stems); + + for (vsize i = 1; i < stems.size (); i++) + { + Grob *s1 = Staff_symbol_referencer::get_staff_symbol (stems[i-1]); + Grob *s2 = Staff_symbol_referencer::get_staff_symbol (stems[i]); + if (s1 != s2) + return SCM_BOOL_T; + } + return SCM_BOOL_F; +} + ADD_INTERFACE (Arpeggio, "Functions and settings for drawing an arpeggio symbol (a wavy line left to noteheads.", diff --git a/lily/axis-group-interface.cc b/lily/axis-group-interface.cc index 7f6d1ef6b5..b62465322e 100644 --- a/lily/axis-group-interface.cc +++ b/lily/axis-group-interface.cc @@ -14,9 +14,11 @@ #include "grob-array.hh" #include "hara-kiri-group-spanner.hh" #include "international.hh" +#include "lookup.hh" #include "paper-column.hh" #include "paper-score.hh" #include "separation-item.hh" +#include "stencil.hh" #include "system.hh" #include "warn.hh" @@ -208,7 +210,16 @@ Axis_group_interface::calc_skylines (SCM smob) { Grob *me = unsmob_grob (smob); extract_grob_set (me, "elements", elts); - return skyline_spacing (me, elts).smobbed_copy (); + Skyline_pair skylines = skyline_spacing (me, elts); + + /* add a minimum-Y-extent-sized box to the skyline */ + SCM min_y_extent = me->get_property ("minimum-Y-extent"); + if (is_number_pair (min_y_extent)) + { + Box b (me->extent (me, X_AXIS), ly_scm2interval (min_y_extent)); + skylines.insert (b, 0, X_AXIS); + } + return skylines.smobbed_copy (); } /* whereas calc_skylines calculates skylines for axis-groups with a lot of @@ -230,7 +241,7 @@ Axis_group_interface::combine_skylines (SCM smob) Skyline_pair ret; for (vsize i = 0; i < elements.size (); i++) { - SCM skyline_scm = elements[i]->get_property ("skylines"); + SCM skyline_scm = elements[i]->get_property ("vertical-skylines"); if (Skyline_pair::unsmob (skyline_scm)) { Real offset = elements[i]->relative_coordinate (y_common, Y_AXIS); @@ -246,7 +257,8 @@ SCM Axis_group_interface::generic_group_extent (Grob *me, Axis a) { /* trigger the callback to do skyline-spacing on the children */ - (void) me->get_property ("skylines"); + if (a == Y_AXIS) + (void) me->get_property ("vertical-skylines"); extract_grob_set (me, "elements", elts); Grob *common = common_refpoint_of_array (elts, me, a); @@ -372,18 +384,27 @@ staff_priority_less (Grob * const &g1, Grob * const &g2) } static void -add_boxes (Grob *me, Grob *x_common, Grob *y_common, vector *const boxes) +add_boxes (Grob *me, Grob *x_common, Grob *y_common, vector *const boxes, Skyline_pair *skylines) { - /* if we are a parent, consider the children's boxes instead of mine */ - if (Grob_array *elements = unsmob_grob_array (me->get_object ("elements"))) + /* if a child has skylines, use them instead of the extent box */ + if (Skyline_pair *pair = Skyline_pair::unsmob (me->get_property ("vertical-skylines"))) + { + Skyline_pair s = *pair; + s.shift (me->relative_coordinate (x_common, X_AXIS)); + s.raise (me->relative_coordinate (y_common, Y_AXIS)); + skylines->merge (s); + } + else if (Grob_array *elements = unsmob_grob_array (me->get_object ("elements"))) { for (vsize i = 0; i < elements->size (); i++) - add_boxes (elements->grob (i), x_common, y_common, boxes); + add_boxes (elements->grob (i), x_common, y_common, boxes, skylines); } else if (!scm_is_number (me->get_property ("outside-staff-priority")) && !to_boolean (me->get_property ("cross-staff"))) - boxes->push_back (Box (me->extent (x_common, X_AXIS), - me->extent (y_common, Y_AXIS))); + { + boxes->push_back (Box (me->extent (x_common, X_AXIS), + me->extent (y_common, Y_AXIS))); + } } /* We want to avoid situations like this: @@ -475,11 +496,14 @@ Axis_group_interface::skyline_spacing (Grob *me, vector elements) vsize i = 0; vector boxes; + Skyline_pair skylines; for (i = 0; i < elements.size () && !scm_is_number (elements[i]->get_property ("outside-staff-priority")); i++) - add_boxes (elements[i], x_common, y_common, &boxes); + add_boxes (elements[i], x_common, y_common, &boxes, &skylines); - Skyline_pair skylines (boxes, 0, X_AXIS); + SCM padding_scm = me->get_property ("skyline-horizontal-padding"); + Real padding = robust_scm2double (padding_scm, 0.1); + skylines.merge (Skyline_pair (boxes, padding, X_AXIS)); for (; i < elements.size (); i++) { SCM priority = elements[i]->get_property ("outside-staff-priority"); @@ -491,6 +515,7 @@ Axis_group_interface::skyline_spacing (Grob *me, vector elements) add_grobs_of_one_priority (&skylines, current_elts, x_common, y_common); } + skylines.shift (-me->relative_coordinate (x_common, X_AXIS)); return skylines; } @@ -509,6 +534,24 @@ Axis_group_interface::calc_max_stretch (SCM smob) return scm_from_double (ret); } +extern bool debug_skylines; +MAKE_SCHEME_CALLBACK (Axis_group_interface, print, 1) +SCM +Axis_group_interface::print (SCM smob) +{ + if (!debug_skylines) + return SCM_BOOL_F; + + Grob *me = unsmob_grob (smob); + Stencil ret; + if (Skyline_pair *s = Skyline_pair::unsmob (me->get_property ("vertical-skylines"))) + { + ret.add_stencil (Lookup::points_to_line_stencil (0.1, (*s)[UP].to_points (X_AXIS)).in_color (255, 0, 255)); + ret.add_stencil (Lookup::points_to_line_stencil (0.1, (*s)[DOWN].to_points (X_AXIS)).in_color (0, 255, 255)); + } + return ret.smobbed_copy (); +} + ADD_INTERFACE (Axis_group_interface, "An object that groups other layout objects.", @@ -522,6 +565,6 @@ ADD_INTERFACE (Axis_group_interface, "max-stretch " "pure-Y-common " "pure-relevant-elements " - "skylines " + "vertical-skylines " "cached-pure-extents " ); diff --git a/lily/include/arpeggio.hh b/lily/include/arpeggio.hh index b58fd9e299..7b0fe264b6 100644 --- a/lily/include/arpeggio.hh +++ b/lily/include/arpeggio.hh @@ -19,6 +19,9 @@ public: DECLARE_SCHEME_CALLBACK (print, (SCM)); DECLARE_SCHEME_CALLBACK (brew_chord_bracket, (SCM)); DECLARE_SCHEME_CALLBACK (width, (SCM)); + DECLARE_SCHEME_CALLBACK (height, (SCM)); + DECLARE_SCHEME_CALLBACK (pure_height, (SCM, SCM, SCM)); + DECLARE_SCHEME_CALLBACK (calc_cross_staff, (SCM)); DECLARE_GROB_INTERFACE(); }; diff --git a/lily/include/axis-group-interface.hh b/lily/include/axis-group-interface.hh index 2f4f501cee..de6cd01ad7 100644 --- a/lily/include/axis-group-interface.hh +++ b/lily/include/axis-group-interface.hh @@ -26,6 +26,7 @@ struct Axis_group_interface DECLARE_SCHEME_CALLBACK (calc_skylines, (SCM smob)); DECLARE_SCHEME_CALLBACK (combine_skylines, (SCM smob)); DECLARE_SCHEME_CALLBACK (calc_max_stretch, (SCM smob)); + DECLARE_SCHEME_CALLBACK (print, (SCM smob)); static Interval relative_group_extent (vector const &list, Grob *common, Axis); static Interval relative_pure_height (Grob *me, vector const &list, diff --git a/lily/include/separation-item.hh b/lily/include/separation-item.hh index 5bcb4e6e3e..1b2471118d 100644 --- a/lily/include/separation-item.hh +++ b/lily/include/separation-item.hh @@ -16,8 +16,9 @@ struct Separation_item { - DECLARE_GROB_INTERFACE(); - DECLARE_SCHEME_CALLBACK(calc_skylines, (SCM)); + DECLARE_GROB_INTERFACE (); + DECLARE_SCHEME_CALLBACK (calc_skylines, (SCM)); + DECLARE_SCHEME_CALLBACK (print, (SCM)); static vector boxes (Grob *me, Grob *left); static Skyline conditional_skyline (Grob *, Grob *); diff --git a/lily/include/skyline.hh b/lily/include/skyline.hh index bccac37238..31747b1d43 100644 --- a/lily/include/skyline.hh +++ b/lily/include/skyline.hh @@ -20,13 +20,11 @@ struct Building { - Interval iv_; - Drul_array height_; - + Real end_; Real y_intercept_; Real slope_; - void precompute (); + void precompute (Real start, Real start_height, Real end_height, Real end); Building (Real start, Real start_height, Real end_height, Real end); Building (Box const &b, Real horizon_padding, Axis a, Direction d); void print () const; @@ -34,15 +32,8 @@ struct Building Real height (Real x) const; Real intersection_x (Building const &other) const; void leading_part (Real chop); - bool conceals_beginning (Building const &other) const; - bool conceals (Building const &other) const; - bool sane () const; - Building sloped_neighbour (Real horizon_padding, Direction d) const; - - bool operator< (Building const &other) - { - return iv_[LEFT] < other.iv_[LEFT]; - } + bool conceals (Building const &other, Real x) const; + Building sloped_neighbour (Real start, Real horizon_padding, Direction d) const; }; class Skyline @@ -53,7 +44,7 @@ private: void internal_merge_skyline (list*, list*, list *const result); - list internal_build_skyline (list*); + list internal_build_skyline (list*, Real, Axis, Direction); DECLARE_SIMPLE_SMOBS(Skyline); public: @@ -62,7 +53,7 @@ public: Skyline (Direction sky); Skyline (vector const &bldgs, Real horizon_padding, Axis a, Direction sky); Skyline (Box const &b, Real horizon_padding, Axis a, Direction sky); - vector to_points () const; + vector to_points (Axis) const; void merge (Skyline const &); void insert (Box const &, Real horizon_padding, Axis); void print () const; diff --git a/lily/include/system.hh b/lily/include/system.hh index 3a5b39ad4e..918243d0a0 100644 --- a/lily/include/system.hh +++ b/lily/include/system.hh @@ -20,8 +20,6 @@ class System : public Spanner { int rank_; Grob_array *all_elements_; - Skyline_pair skylines_; - void build_skylines (); void init_elements (); friend class Paper_score; // ugh. Paper_score *pscore_; // ugh. diff --git a/lily/prob-scheme.cc b/lily/prob-scheme.cc index 3f729e4894..7e0b560912 100644 --- a/lily/prob-scheme.cc +++ b/lily/prob-scheme.cc @@ -94,8 +94,8 @@ LY_DEFINE (ly_paper_system_minimum_distance, "ly:paper-system-minimum-distance", Real ret = 0; Prob *p1 = unsmob_prob (sys1); Prob *p2 = unsmob_prob (sys2); - Skyline_pair *sky1 = Skyline_pair::unsmob (p1->get_property ("skylines")); - Skyline_pair *sky2 = Skyline_pair::unsmob (p2->get_property ("skylines")); + Skyline_pair *sky1 = Skyline_pair::unsmob (p1->get_property ("vertical-skylines")); + Skyline_pair *sky2 = Skyline_pair::unsmob (p2->get_property ("vertical-skylines")); if (sky1 && sky2) ret = (*sky1)[DOWN].distance ((*sky2)[UP]); diff --git a/lily/separation-item.cc b/lily/separation-item.cc index 0e3a075100..8a83e2b7a1 100644 --- a/lily/separation-item.cc +++ b/lily/separation-item.cc @@ -8,6 +8,8 @@ #include "separation-item.hh" +#include "lookup.hh" +#include "stencil.hh" #include "skyline.hh" #include "paper-column.hh" #include "warn.hh" @@ -31,8 +33,8 @@ void Separation_item::set_skyline_distance (Drul_array items, Real padding) { - Drul_array lines (Skyline_pair::unsmob (items[LEFT]->get_property ("skylines")), - Skyline_pair::unsmob (items[RIGHT]->get_property ("skylines"))); + Drul_array lines (Skyline_pair::unsmob (items[LEFT]->get_property ("horizontal-skylines")), + Skyline_pair::unsmob (items[RIGHT]->get_property ("horizontal-skylines"))); Skyline right = conditional_skyline (items[RIGHT], items[LEFT]); right.merge ((*lines[RIGHT])[LEFT]); @@ -179,6 +181,24 @@ Separation_item::extremal_break_aligned_grob (Grob *me, return last_grob; } +extern bool debug_skylines; +MAKE_SCHEME_CALLBACK (Separation_item, print, 1) +SCM +Separation_item::print (SCM smob) +{ + if (!debug_skylines) + return SCM_BOOL_F; + + Grob *me = unsmob_grob (smob); + Stencil ret; + if (Skyline_pair *s = Skyline_pair::unsmob (me->get_property ("horizontal-skylines"))) + { + ret.add_stencil (Lookup::points_to_line_stencil (0.1, (*s)[LEFT].to_points (Y_AXIS)).in_color (255, 255, 0)); + ret.add_stencil (Lookup::points_to_line_stencil (0.1, (*s)[RIGHT].to_points (Y_AXIS)).in_color (0, 255, 255)); + } + return ret.smobbed_copy (); +} + ADD_INTERFACE (Separation_item, "Item that computes widths to generate spacing rods. " "This is done in concert with @ref{separating-group-spanner-interface}.", @@ -187,5 +207,5 @@ ADD_INTERFACE (Separation_item, "conditional-elements " "elements " "padding " - "skylines " + "horizontal-skylines " ); diff --git a/lily/skyline.cc b/lily/skyline.cc index 99452189c6..8c47df70b8 100644 --- a/lily/skyline.cc +++ b/lily/skyline.cc @@ -42,169 +42,94 @@ but the distance routine does. */ -/* - FIXME: - - * Consider to use - - typedef list Skyline; - struct Skyline_point - { - Real x; - Drul_array ys; - }; - - this is a cleaner representation, as it doesn't duplicate the X, and - doesn't need bogus buildings at infinity --hwn. - - - * All the messing around with EPS is very fishy. There are no - complicated numerical algorithms involved, so EPS should not be - necessary. - - --hwn - - - */ - -#define EPS 1e-10 - -static inline bool -approx_equal (Real x, Real y) -{ - return abs (x - y) < EPS || (isinf (x) && isinf (y) && ((x > 0) == (y > 0))); -} - -static inline bool -approx_greater_than (Real x, Real y) -{ - return x > y + EPS; -} +/* If we start including very thin buildings, numerical accuracy errors can + arise. Therefore, we ignore all buildings that are less than epsilon wide. */ +#define EPS 1e-5 -static inline bool -approx_less_than (Real x, Real y) -{ - return x < y - EPS; -} - -static inline bool -approx_less_equal (Real x, Real y) -{ - return x <= y + EPS; -} - -static inline bool -approx_greater_equal (Real x, Real y) +static void +print_buildings (list const &b) { - return x >= y - EPS; + for (list::const_iterator i = b.begin (); i != b.end (); i++) + i->print (); } void Skyline::print () const { - for (list::const_iterator i = buildings_.begin (); - i != buildings_.end (); i++) - { - (*i).print (); - } -} - -bool -is_legal_skyline (list const &buildings) -{ - list::const_iterator i; - Real last_x = -infinity_f; - for (i = buildings.begin (); i != buildings.end (); i++) - { - if (i->iv_[LEFT] != last_x) - return false; - last_x = i->iv_[RIGHT]; - if (isinf (i->iv_.length ()) && i->height_[LEFT] != i->height_[RIGHT]) - return false; - } - return last_x == infinity_f; + print_buildings (buildings_); } Building::Building (Real start, Real start_height, Real end_height, Real end) - : iv_ (start, end) { - height_[LEFT] = start_height; - height_[RIGHT] = end_height; - if (isinf (start) || isinf (end)) assert (start_height == end_height); - precompute (); + end_ = end; + precompute (start, start_height, end_height, end); } Building::Building (Box const &b, Real horizon_padding, Axis horizon_axis, Direction sky) { + Real start = b[horizon_axis][LEFT] - horizon_padding; + Real end = b[horizon_axis][RIGHT] + horizon_padding; Real height = sky * b[other_axis (horizon_axis)][sky]; - iv_ = b[horizon_axis]; - iv_.widen (horizon_padding + EPS); - height_[LEFT] = height; - height_[RIGHT] = height; - - if (sane ()) - precompute (); + end_ = end; + precompute (start, height, height, end); } void -Building::precompute () +Building::precompute (Real start, Real start_height, Real end_height, Real end) { - slope_ = (height_[RIGHT] - height_[LEFT]) / (iv_.length ()); - if (height_[LEFT] == height_[RIGHT]) /* in case they're both infinity */ + slope_ = (end_height - start_height) / (end - start); + if (start_height == end_height) /* if they were both infinite, we would get nan, not 0, from the prev line */ slope_ = 0; assert (!isinf (slope_) && !isnan (slope_)); - if (isinf (iv_[START])) + if (isinf (start)) { - assert (slope_ == 0); - y_intercept_ = height_[LEFT]; + assert (start_height == end_height); + y_intercept_ = start_height; } else - y_intercept_ = height_[LEFT] - slope_ * iv_[START]; + y_intercept_ = start_height - slope_ * start; } Real Building::height (Real x) const { - if (isinf (x)) - return (x > 0) ? height_[RIGHT] : height_[LEFT]; - return slope_*x + y_intercept_; + return isinf (x) ? y_intercept_ : slope_*x + y_intercept_; } void Building::print () const { - printf ("X[%f,%f] -> Y[%f,%f]\n", - iv_[LEFT], iv_[RIGHT], - height_[LEFT], height_[RIGHT]); + printf ("%f x + %f ends at %f\n", slope_, y_intercept_, end_); } Real Building::intersection_x (Building const &other) const { - return (y_intercept_ - other.y_intercept_) / (other.slope_ - slope_); + Real ret = (y_intercept_ - other.y_intercept_) / (other.slope_ - slope_); + return isnan (ret) ? -infinity_f : ret; } void Building::leading_part (Real chop) { - assert (chop > iv_[LEFT] && chop <= iv_[RIGHT] && !approx_equal (chop, iv_[LEFT])); - iv_[RIGHT] = chop; - height_[RIGHT] = height (chop); + assert (chop <= end_); + end_ = chop; } Building -Building::sloped_neighbour (Real horizon_padding, Direction d) const +Building::sloped_neighbour (Real start, Real horizon_padding, Direction d) const { - Real left = iv_[d]; - Real right = iv_[d] + d * horizon_padding; - Real left_height = height_[d]; - Real right_height = height_[d] - horizon_padding; + Real x = (d == LEFT) ? start : end_; + Real left = x; + Real right = x + d * horizon_padding; + Real left_height = height (x); + Real right_height = left_height - horizon_padding; if (d == LEFT) { swap (left, right); @@ -213,88 +138,68 @@ Building::sloped_neighbour (Real horizon_padding, Direction d) const return Building (left, left_height, right_height, right); } -bool -Building::sane () const +static Real +first_intersection (Building const &b, list *const s, Real start_x) { - return approx_less_than (iv_[LEFT], iv_[RIGHT]) - && !isinf (height_[RIGHT]) - && !isinf (height_[LEFT]); -} + while (!s->empty () && start_x < b.end_) + { + Building c = s->front (); + if (c.conceals (b, start_x)) + return start_x; -static void -skyline_trailing_part (list *sky, Real x) -{ - if (approx_equal (x, sky->front ().iv_[RIGHT])) - sky->pop_front (); - else - assert (x < sky->front ().iv_[RIGHT]); + Real i = b.intersection_x (c); + if (i > start_x && i <= b.end_ && i <= c.end_) + return i; - if (!sky->empty ()) - { - sky->front ().iv_[LEFT] = x; - sky->front ().height_[LEFT] = sky->front ().height (x); + start_x = c.end_; + if (b.end_ > c.end_) + s->pop_front (); } + return b.end_; } bool -Building::conceals_beginning (Building const &other) const +Building::conceals (Building const &other, Real x) const { - bool w = false; - Real h = other.height (iv_[LEFT]); - if (approx_equal (height_[LEFT], h)) - w = slope_ > other.slope_; - else if (height_[LEFT] > h) - w = true; - else - w = false; - - return w; -} + if (slope_ == other.slope_) + return y_intercept_ > other.y_intercept_; -bool -Building::conceals (Building const &other) const -{ - assert (iv_[LEFT] <= other.iv_[LEFT]); - return (iv_[RIGHT] >= other.iv_[RIGHT]) - && approx_greater_equal (height (other.iv_[LEFT]), other.height_[LEFT]) - && approx_greater_equal (height (other.iv_[RIGHT]), other.height_[RIGHT]); + /* their slopes were not equal, so there is an intersection point */ + Real i = intersection_x (other); + return (i < x && slope_ > other.slope_) + || (i > x && slope_ < other.slope_); } void Skyline::internal_merge_skyline (list *s1, list *s2, list *const result) { + Real x = -infinity_f; while (!s1->empty ()) { - if (s2->front ().conceals_beginning (s1->front ())) + if (s2->front ().conceals (s1->front (), x)) swap (s1, s2); Building b = s1->front (); - while (!s2->empty () && b.conceals (s2->front ())) - s2->pop_front (); + Real end = first_intersection (b, s2, x); + if (s2->empty ()) { result->push_front (b); break; } - /* s2 either intersects with b or it ends after b */ - Real end = infinity_f; - Real s2_start_height = s2->front ().height_[LEFT]; - Real s2_end_height = s2->front ().height_[RIGHT]; - Real s1_start_height = b.height (s2->front ().iv_[LEFT]); - Real s1_end_height = b.height (s2->front ().iv_[RIGHT]); - if (approx_greater_than (s2_start_height, s1_start_height)) - end = s2->front ().iv_[LEFT]; - else if (approx_greater_than (s2_end_height, s1_end_height)) - end = b.intersection_x (s2->front ()); - end = min (end, b.iv_[RIGHT]); - - b.leading_part (end); - result->push_front (b); - - skyline_trailing_part (s1, end); - skyline_trailing_part (s2, end); + /* only include buildings wider than epsilon */ + if (end > x + EPS) + { + b.leading_part (end); + result->push_front (b); + } + + if (end >= s1->front ().end_) + s1->pop_front (); + + x = end; } result->reverse (); } @@ -306,62 +211,85 @@ empty_skyline (list *const ret) } static void -single_skyline (Building b, Real horizon_padding, list *const ret) +single_skyline (Building b, Real start, Real horizon_padding, list *const ret) { - b.iv_.widen (horizon_padding); - - if (!isinf (b.iv_[RIGHT])) - ret->push_front (Building (b.iv_[RIGHT], -infinity_f, + bool sloped_neighbours = horizon_padding > 0 && !isinf (start) && !isinf (b.end_); + if (!isinf (b.end_)) + ret->push_front (Building (b.end_ + horizon_padding, -infinity_f, -infinity_f, infinity_f)); - if (horizon_padding > 0 && !isinf (b.iv_.length ())) - ret->push_front (b.sloped_neighbour (horizon_padding, RIGHT)); + if (sloped_neighbours) + ret->push_front (b.sloped_neighbour (start, horizon_padding, RIGHT)); - if (b.iv_[RIGHT] > b.iv_[LEFT]) + if (b.end_ > start + EPS) ret->push_front (b); - if (horizon_padding > 0 && !isinf (b.iv_.length ())) - ret->push_front (b.sloped_neighbour (horizon_padding, LEFT)); - if (!isinf (b.iv_[LEFT])) + if (sloped_neighbours) + ret->push_front (b.sloped_neighbour (start, horizon_padding, LEFT)); + + if (!isinf (start)) ret->push_front (Building (-infinity_f, -infinity_f, - -infinity_f, b.iv_[LEFT])); + -infinity_f, start - horizon_padding)); } -/* remove a non-overlapping set of buildings from BUILDINGS and build a skyline +/* remove a non-overlapping set of boxes from BOXES and build a skyline out of them */ static list -non_overlapping_skyline (list *const buildings) +non_overlapping_skyline (list *const boxes, Real horizon_padding, Axis horizon_axis, Direction sky) { list result; Real last_end = -infinity_f; - list::iterator i = buildings->begin (); - while (i != buildings->end ()) + list::iterator i = boxes->begin (); + while (i != boxes->end ()) { - if (approx_less_than (i->iv_[LEFT], last_end)) + Interval iv = (*i)[horizon_axis]; + + if (iv[LEFT] - horizon_padding < last_end) { i++; continue; } - if (approx_greater_than (i->iv_[LEFT], last_end)) - result.push_back (Building (last_end, -infinity_f, -infinity_f, i->iv_[LEFT])); - else - i->iv_[LEFT] = last_end; + if (iv[LEFT] - horizon_padding > last_end + EPS) + result.push_front (Building (last_end, -infinity_f, -infinity_f, iv[LEFT] - horizon_padding)); + + Building b (*i, horizon_padding, horizon_axis, sky); + bool sloped_neighbours = horizon_padding > 0 && !isinf (iv.length ()); + if (sloped_neighbours) + result.push_front (b.sloped_neighbour (iv[LEFT] - horizon_padding, horizon_padding, LEFT)); + result.push_front (b); + if (sloped_neighbours) + result.push_front (b.sloped_neighbour (iv[LEFT] - horizon_padding, horizon_padding, RIGHT)); - last_end = i->iv_[RIGHT]; - list::iterator j = i; - i++; - result.splice (result.end (), *buildings, j); + list::iterator j = i++; + boxes->erase (j); + last_end = result.front ().end_; } if (last_end < infinity_f) - result.push_back (Building (last_end, -infinity_f, -infinity_f, infinity_f)); - assert (is_legal_skyline (result)); + result.push_front (Building (last_end, -infinity_f, -infinity_f, infinity_f)); + result.reverse (); return result; } +class LessThanBox +{ + Axis a_; + +public: + LessThanBox (Axis a) + { + a_ = a; + } + + bool operator() (Box const &b1, Box const &b2) + { + return b1[a_][LEFT] < b2[a_][LEFT]; + } +}; + list -Skyline::internal_build_skyline (list *buildings) +Skyline::internal_build_skyline (list *boxes, Real horizon_padding, Axis horizon_axis, Direction sky) { - vsize size = buildings->size (); + vsize size = boxes->size (); if (size == 0) { @@ -372,14 +300,15 @@ Skyline::internal_build_skyline (list *buildings) else if (size == 1) { list result; - single_skyline (buildings->front (), 0, &result); + single_skyline (Building (boxes->front (), horizon_padding, horizon_axis, sky), + boxes->front ()[horizon_axis][LEFT], horizon_axis, &result); return result; } deque > partials; - buildings->sort (); - while (!buildings->empty ()) - partials.push_back (non_overlapping_skyline (buildings)); + boxes->sort (LessThanBox (horizon_axis)); + while (!boxes->empty ()) + partials.push_back (non_overlapping_skyline (boxes, horizon_padding, horizon_axis, sky)); /* we'd like to say while (partials->size () > 1) but that's O (n). Instead, we exit in the middle of the loop */ @@ -410,6 +339,7 @@ Skyline::Skyline (Skyline const &src) { sky_ = src.sky_; + /* doesn't a list's copy constructor do this? -- jneem */ for (list::const_iterator i = src.buildings_.begin (); i != src.buildings_.end (); i++) { @@ -434,32 +364,26 @@ Skyline::Skyline (Direction sky) */ Skyline::Skyline (vector const &boxes, Real horizon_padding, Axis horizon_axis, Direction sky) { - list bldgs; + list filtered_boxes; sky_ = sky; + Axis vert_axis = other_axis (horizon_axis); for (vsize i = 0; i < boxes.size (); i++) { - Building front (boxes[i], horizon_padding, horizon_axis, sky); - if (front.sane ()) - { - bldgs.push_front (front); - if (horizon_padding > 0 && !isinf (front.iv_.length ())) - { - bldgs.push_front (front.sloped_neighbour (horizon_padding, LEFT)); - bldgs.push_front (front.sloped_neighbour (horizon_padding, RIGHT)); - } - } + Interval iv = boxes[i][horizon_axis]; + iv.widen (horizon_padding); + if (iv.length () > EPS && !boxes[i][vert_axis].is_empty ()) + filtered_boxes.push_front (boxes[i]); } - buildings_ = internal_build_skyline (&bldgs); - assert (is_legal_skyline (buildings_)); + buildings_ = internal_build_skyline (&filtered_boxes, horizon_padding, horizon_axis, sky); } Skyline::Skyline (Box const &b, Real horizon_padding, Axis horizon_axis, Direction sky) { sky_ = sky; - Building front (b, 0, horizon_axis, sky); - single_skyline (front, horizon_padding, &buildings_); + Building front (b, horizon_padding, horizon_axis, sky); + single_skyline (front, b[horizon_axis][LEFT], horizon_padding, &buildings_); } void @@ -471,7 +395,6 @@ Skyline::merge (Skyline const &other) list my_bld; my_bld.splice (my_bld.begin (), buildings_); internal_merge_skyline (&other_bld, &my_bld, &buildings_); - assert (is_legal_skyline (buildings_)); } void @@ -481,9 +404,8 @@ Skyline::insert (Box const &b, Real horizon_padding, Axis a) list my_bld; my_bld.splice (my_bld.begin (), buildings_); - single_skyline (Building (b, 0, a, sky_), horizon_padding, &other_bld); + single_skyline (Building (b, horizon_padding, a, sky_), b[a][LEFT], horizon_padding, &other_bld); internal_merge_skyline (&other_bld, &my_bld, &buildings_); - assert (is_legal_skyline (buildings_)); } void @@ -491,22 +413,17 @@ Skyline::raise (Real r) { list::iterator end = buildings_.end (); for (list::iterator i = buildings_.begin (); i != end; i++) - { - i->height_[LEFT] += sky_ * r; - i->height_[RIGHT] += sky_ * r; - i->y_intercept_ += sky_ * r; - } - assert (is_legal_skyline (buildings_)); + i->y_intercept_ += sky_ * r; } void -Skyline::shift (Real r) +Skyline::shift (Real s) { list::iterator end = buildings_.end (); for (list::iterator i = buildings_.begin (); i != end; i++) { - i->iv_[LEFT] += r; - i->iv_[RIGHT] += r; + i->end_ += s; + i->y_intercept_ -= s * i->slope_; } } @@ -518,15 +435,18 @@ Skyline::distance (Skyline const &other) const list::const_iterator j = other.buildings_.begin (); Real dist = -infinity_f; + Real start = -infinity_f; while (i != buildings_.end () && j != other.buildings_.end ()) { - Interval iv = intersection (i->iv_, j->iv_); - dist = max (dist, max (i->height (iv[LEFT]) + j->height (iv[LEFT]), - i->height (iv[RIGHT]) + j->height (iv[RIGHT]))); - if (i->iv_[RIGHT] <= j->iv_[RIGHT]) + Real end = min (i->end_, j->end_); + Real start_dist = i->height (start) + j->height (start); + Real end_dist = i->height (end) + j->height (end); + dist = max (dist, max (start_dist, end_dist)); + if (i->end_ <= j->end_) i++; else j++; + start = end; } return dist; } @@ -539,7 +459,7 @@ Skyline::height (Real airplane) const list::const_iterator i; for (i = buildings_.begin (); i != buildings_.end (); i++) { - if (i->iv_[RIGHT] >= airplane) + if (i->end_ >= airplane) return sky_ * i->height (airplane); } @@ -559,26 +479,29 @@ void Skyline::set_minimum_height (Real h) { Skyline s (sky_); - s.buildings_.front ().height_[LEFT] = h * sky_; - s.buildings_.front ().height_[RIGHT] = h * sky_; s.buildings_.front ().y_intercept_ = h * sky_; merge (s); } vector -Skyline::to_points () const +Skyline::to_points (Axis a) const { vector out; + Real start = -infinity_f; for (list::const_iterator i (buildings_.begin ()); i != buildings_.end (); i++) { - if (!isinf (i->iv_[LEFT]) && !isinf (i->height_[LEFT])) - out.push_back (Offset (i->iv_[LEFT], sky_ * i->height_[LEFT])); - if (!isinf (i->iv_[RIGHT]) && !isinf (i->height_[RIGHT])) - out.push_back (Offset (i->iv_[RIGHT], sky_ * i->height_[RIGHT])); + out.push_back (Offset (start, sky_ * i->height (start))); + out.push_back (Offset (i->end_, sky_ * i->height (i->end_))); + start = i->end_; } + + if (a == Y_AXIS) + for (vsize i = 0; i < out.size (); i++) + out[i] = Offset (out[i][Y_AXIS], out[i][X_AXIS]); + return out; } diff --git a/lily/system.cc b/lily/system.cc index 6e19d0a6f4..663d654e0b 100644 --- a/lily/system.cc +++ b/lily/system.cc @@ -340,7 +340,6 @@ System::get_paper_system () SCM *tail = &exprs; post_processing (); - build_skylines (); vector entries; for (vsize j = 0; j < all_elements_->size (); j++) @@ -388,8 +387,12 @@ System::get_paper_system () exprs)); if (debug_skylines) { - sys_stencil.add_stencil (Lookup::points_to_line_stencil (0.1, skylines_[UP].to_points ()).in_color (255, 0, 0)); - sys_stencil.add_stencil (Lookup::points_to_line_stencil (0.1, skylines_[DOWN].to_points ()).in_color (0, 255, 0)); + Skyline_pair *skylines = Skyline_pair::unsmob (get_property ("vertical-skylines")); + if (skylines) + { + sys_stencil.add_stencil (Lookup::points_to_line_stencil (0.1, (*skylines)[UP].to_points (X_AXIS)).in_color (255, 0, 0)); + sys_stencil.add_stencil (Lookup::points_to_line_stencil (0.1, (*skylines)[DOWN].to_points (X_AXIS)).in_color (0, 255, 0)); + } } Grob *left_bound = this->get_bound (LEFT); @@ -399,7 +402,7 @@ System::get_paper_system () /* information that the page breaker might need */ Grob *right_bound = this->get_bound (RIGHT); - pl->set_property ("skylines", skylines_.smobbed_copy ()); + pl->set_property ("skylines", this->get_property ("skylines")); pl->set_property ("page-break-permission", right_bound->get_property ("page-break-permission")); pl->set_property ("page-turn-permission", right_bound->get_property ("page-turn-permission")); pl->set_property ("page-break-penalty", right_bound->get_property ("page-break-penalty")); @@ -503,29 +506,6 @@ get_root_system (Grob *me) return dynamic_cast (system_grob); } -void -System::build_skylines () -{ - vector boxes; - for (vsize i = 0; i < all_elements_->size (); i++) - { - Grob *g = all_elements_->grob (i); - if (!unsmob_stencil (g->get_property ("stencil"))) - continue; - - Interval xiv = g->extent (this, X_AXIS); - Interval yiv = g->extent (this, Y_AXIS); - if (!xiv.is_empty () && !yiv.is_empty ()) - boxes.push_back (Box (xiv, yiv)); - } - - SCM horizon_padding_scm = get_property ("skyline-horizontal-padding"); - Real horizon_padding = robust_scm2double (horizon_padding_scm, 0); - skylines_[UP] = Skyline (boxes, horizon_padding, X_AXIS, UP); - skylines_[DOWN] = Skyline (boxes, horizon_padding, X_AXIS, DOWN); -} - - ADD_INTERFACE (System, "This is the toplevel object: each object in a score " "ultimately has a System object as its X and Y parent. ", diff --git a/scm/define-grob-properties.scm b/scm/define-grob-properties.scm index 7cf4d853f8..4c8cc6ef40 100644 --- a/scm/define-grob-properties.scm +++ b/scm/define-grob-properties.scm @@ -357,7 +357,8 @@ note that starts here.") object.") (side-axis ,number? "If the value is #X (or equivalently 1), the object is placed horizontally next to the other object. If the value is #Y or 0, it is placed vertically.") (size ,number? "Size of object, relative to standard size.") - (skylines ,ly:skyline-pair? "Two skylines, one above and one below this grob (or, for some grobs, to the left and to the right).") + (vertical-skylines ,ly:skyline-pair? "Two skylines, one above and one below this grob.") + (horizontal-skylines ,ly:skyline-pair? "Two skylines, one to the left and one to the right of this grob.") (slope ,number? "The slope of this object.") (slur-padding ,number? "Extra distance between slur and script.") (space-alist ,list? "A table that specifies distances between diff --git a/scm/define-grobs.scm b/scm/define-grobs.scm index 8d81855a1b..9497794b80 100644 --- a/scm/define-grobs.scm +++ b/scm/define-grobs.scm @@ -153,6 +153,8 @@ (script-priority . 0) (side-axis . ,X) (staff-position . 0.0) + (cross-staff . ,ly:arpeggio::calc-cross-staff) + (Y-extent . ,ly:arpeggio::height) (meta . ((class . Item) (interfaces . (arpeggio-interface staff-symbol-referencer-interface @@ -290,7 +292,7 @@ . ( (axes . (,Y)) (Y-extent . ,ly:axis-group-interface::height) - (skylines . ,ly:axis-group-interface::calc-skylines) + (vertical-skylines . ,ly:axis-group-interface::calc-skylines) (meta . ((class . Spanner) (interfaces . (axis-group-interface )))))) @@ -1430,7 +1432,8 @@ (avoid-slur . inside) (X-extent . ,ly:axis-group-interface::width) (Y-extent . ,ly:axis-group-interface::height) - (skylines . ,ly:separation-item::calc-skylines) + (horizontal-skylines . ,ly:separation-item::calc-skylines) + (stencil . ,ly:separation-item::print) (meta . ((class . Item) (interfaces . ( separation-item-interface)))))) @@ -1704,7 +1707,7 @@ (axes . (0 1)) (X-extent . ,ly:axis-group-interface::width) (Y-extent . ,ly:axis-group-interface::height) - (skylines . ,ly:axis-group-interface::calc-skylines) + (vertical-skylines . ,ly:axis-group-interface::calc-skylines) (max-stretch . ,ly:axis-group-interface::calc-max-stretch) (skyline-horizontal-padding . 1.0) (meta . ((class . System) @@ -2057,7 +2060,7 @@ (X-extent . ,ly:axis-group-interface::width) (stacking-dir . -1) (padding . 0.5) - (skylines . ,ly:axis-group-interface::combine-skylines) + (vertical-skylines . ,ly:axis-group-interface::combine-skylines) (max-stretch . ,ly:align-interface::calc-max-stretch) (meta . ((class . Spanner) (object-callbacks . ((Y-common . ,ly:axis-group-interface::calc-y-common))) @@ -2069,8 +2072,9 @@ (Y-offset . ,ly:hara-kiri-group-spanner::force-hara-kiri-callback) (Y-extent . ,ly:hara-kiri-group-spanner::y-extent) (X-extent . ,ly:axis-group-interface::width) - (skylines . ,ly:hara-kiri-group-spanner::calc-skylines) + (vertical-skylines . ,ly:hara-kiri-group-spanner::calc-skylines) (max-stretch . ,ly:axis-group-interface::calc-max-stretch) + (stencil . ,ly:axis-group-interface::print) (meta . ((class . Spanner) (object-callbacks . ((X-common . ,ly:axis-group-interface::calc-x-common))) (interfaces . (axis-group-interface @@ -2178,6 +2182,10 @@ (define pure-print-callbacks (list + print-circled-text-callback + lyric-text::print + ly:arpeggio::print + ly:arpeggio::brew-chord-bracket ly:bar-line::print ly:note-head::print ly:dots::print @@ -2197,6 +2205,7 @@ (define pure-conversions-alist `( (,ly:accidental-interface::height . ,ly:accidental-interface::pure-height) + (,ly:arpeggio::height . ,ly:arpeggio::pure-height) (,ly:slur::outside-slur-callback . ,ly:slur::pure-outside-slur-callback) (,ly:stem::height . ,ly:stem::pure-height) (,ly:rest::height . ,ly:rest::pure-height)