From: Joe Neeman Date: Sun, 26 Nov 2006 17:11:30 +0000 (+0200) Subject: Merge with git+ssh://jneem@git.sv.gnu.org/srv/git/lilypond.git X-Git-Tag: release/2.11.16-1^2~16^2 X-Git-Url: https://git.donarmstrong.com/?a=commitdiff_plain;h=6f664e0e318a1651f6fe9317a842bf9c7f476f08;hp=2f7343c6c81e75989d087cab3557d6a8527609af;p=lilypond.git Merge with git+ssh://jneem@git.sv.gnu.org/srv/git/lilypond.git --- diff --git a/lily/accidental-placement.cc b/lily/accidental-placement.cc index 02f0c522a2..c943c9cca4 100644 --- a/lily/accidental-placement.cc +++ b/lily/accidental-placement.cc @@ -110,8 +110,8 @@ Accidental_placement::get_relevant_accidental_extent (Grob *me, struct Accidental_placement_entry { - vector left_skyline_; - vector right_skyline_; + Skyline left_skyline_; + Skyline right_skyline_; Interval vertical_extent_; vector extents_; vector grobs_; @@ -307,22 +307,16 @@ Accidental_placement::calc_positioning_done (SCM smob) for (vsize i = apes.size (); i--;) { Accidental_placement_entry *ape = apes[i]; - ape->left_skyline_ = empty_skyline (LEFT); - ape->right_skyline_ = empty_skyline (RIGHT); for (vsize j = apes[i]->grobs_.size (); j--;) { Grob *a = apes[i]->grobs_[j]; - vector boxes = Accidental_interface::accurate_boxes (a, common); ape->extents_.insert (ape->extents_.end (), boxes.begin (), boxes.end ()); - for (vsize j = boxes.size (); j--;) - { - insert_extent_into_skyline (&ape->left_skyline_, boxes[j], Y_AXIS, LEFT); - insert_extent_into_skyline (&ape->right_skyline_, boxes[j], Y_AXIS, RIGHT); - } } + ape->left_skyline_ = Skyline (ape->extents_, Y_AXIS, LEFT); + ape->right_skyline_ = Skyline (ape->extents_, Y_AXIS, RIGHT); } Interval total; @@ -340,16 +334,11 @@ Accidental_placement::calc_positioning_done (SCM smob) Accidental_placement_entry *head_ape = new Accidental_placement_entry; common[X_AXIS] = common_refpoint_of_array (heads, common[X_AXIS], X_AXIS); - vector head_skyline (empty_skyline (LEFT)); vector head_extents; for (vsize i = heads.size (); i--;) - { - Box b (heads[i]->extent (common[X_AXIS], X_AXIS), - heads[i]->extent (common[Y_AXIS], Y_AXIS)); - - insert_extent_into_skyline (&head_skyline, b, Y_AXIS, LEFT); - } + head_extents.push_back (Box (heads[i]->extent (common[X_AXIS], X_AXIS), + heads[i]->extent (common[Y_AXIS], Y_AXIS))); vector stems; for (vsize i = 0; i < heads.size (); i++) @@ -364,27 +353,24 @@ Accidental_placement::calc_positioning_done (SCM smob) { int very_large = INT_MAX; - Box b (heads[i]->extent (common[X_AXIS], X_AXIS), - heads[i]->pure_height (common[Y_AXIS], 0, very_large)); - - insert_extent_into_skyline (&head_skyline, b, Y_AXIS, LEFT); + head_extents.push_back (Box (heads[i]->extent (common[X_AXIS], X_AXIS), + heads[i]->pure_height (common[Y_AXIS], 0, very_large))); } - - head_ape->left_skyline_ = head_skyline; + + head_ape->left_skyline_ = Skyline (head_extents, Y_AXIS, LEFT); head_ape->offset_ = 0.0; Real padding = robust_scm2double (me->get_property ("padding"), 0.2); - vector left_skyline = head_ape->left_skyline_; - heighten_skyline (&left_skyline, - -robust_scm2double (me->get_property ("right-padding"), 0)); + Skyline left_skyline = head_ape->left_skyline_; + left_skyline.raise (-robust_scm2double (me->get_property ("right-padding"), 0)) +; /* Add accs entries right-to-left. */ for (vsize i = apes.size (); i-- > 0;) { - Real offset - = -skyline_meshing_distance (apes[i]->right_skyline_, left_skyline); + Real offset = -apes[i]->right_skyline_.distance (left_skyline); if (isinf (offset)) offset = (i < apes.size () - 1) ? apes[i + 1]->offset_ : 0.0; else @@ -392,9 +378,9 @@ Accidental_placement::calc_positioning_done (SCM smob) apes[i]->offset_ = offset; - vector new_left_skyline = apes[i]->left_skyline_; - heighten_skyline (&new_left_skyline, apes[i]->offset_); - merge_skyline (&new_left_skyline, left_skyline, LEFT); + Skyline new_left_skyline = apes[i]->left_skyline_; + new_left_skyline.raise (apes[i]->offset_); + new_left_skyline.merge (left_skyline); left_skyline = new_left_skyline; } diff --git a/lily/axis-group-interface.cc b/lily/axis-group-interface.cc index 02e900164b..c7a5b8eff9 100644 --- a/lily/axis-group-interface.cc +++ b/lily/axis-group-interface.cc @@ -9,6 +9,7 @@ #include "axis-group-interface.hh" #include "align-interface.hh" +#include "directional-element-interface.hh" #include "pointer-group-interface.hh" #include "grob.hh" #include "grob-array.hh" @@ -17,6 +18,7 @@ #include "item.hh" #include "paper-column.hh" #include "paper-score.hh" +#include "std-vector.hh" #include "system.hh" #include "warn.hh" @@ -182,6 +184,8 @@ SCM Axis_group_interface::generic_group_extent (Grob *me, Axis a) { extract_grob_set (me, "elements", elts); + if (a == Y_AXIS && to_boolean (me->get_property ("skyline-spacing"))) + skyline_spacing (me, elts); Grob *common = common_refpoint_of_array (elts, me, a); Real my_coord = me->relative_coordinate (common, a); @@ -251,6 +255,66 @@ Axis_group_interface::get_children (Grob *me, vector *found) } } +bool +staff_priority_less (Grob * const &g1, Grob * const &g2) +{ + int priority_1 = robust_scm2int (g1->get_property ("outside-staff-priority"), INT_MIN); + int priority_2 = robust_scm2int (g2->get_property ("outside-staff-priority"), INT_MIN); + + if (priority_1 < priority_2) + return true; + else if (priority_1 > priority_2) + return false; + + /* if there is no preference in staff priority, choose the one with the lower rank */ + int rank_1 = g1->spanned_rank_iv ()[LEFT]; + int rank_2 = g2->spanned_rank_iv ()[LEFT]; + return rank_1 < rank_2; +} + +void +Axis_group_interface::skyline_spacing (Grob *me, vector elements) +{ + vector_sort (elements, staff_priority_less); + Grob *x_common = common_refpoint_of_array (elements, me, X_AXIS); + Grob *y_common = common_refpoint_of_array (elements, me, Y_AXIS); + + vsize i = 0; + vector boxes; + + for (i = 0; i < elements.size () + && !scm_is_number (elements[i]->get_property ("outside-staff-priority")); i++) + boxes.push_back (Box (elements[i]->extent (x_common, X_AXIS), + elements[i]->extent (y_common, Y_AXIS))); + + + Drul_array skylines (Skyline (boxes, X_AXIS, DOWN), + Skyline (boxes, X_AXIS, UP)); + for (; i < elements.size (); i++) + { + Direction dir = get_grob_direction (elements[i]); + if (dir == CENTER) + { + warning (_ ("an outside-staff object should have a direction")); + continue; + } + + Box b (elements[i]->extent (x_common, X_AXIS), + elements[i]->extent (y_common, Y_AXIS)); + boxes.clear (); + boxes.push_back (b); + Skyline other = Skyline (boxes, X_AXIS, -dir); + Real dist = skylines[dir].distance (other); + + if (dist > 0) + { + b.translate (Offset (0, dir*dist)); + elements[i]->translate_axis (dir*dist, Y_AXIS); + } + skylines[dir].insert (b, X_AXIS); + } +} + ADD_INTERFACE (Axis_group_interface, "axis-group-interface", "An object that groups other layout objects.", @@ -260,5 +324,6 @@ ADD_INTERFACE (Axis_group_interface, "axis-group-interface", "elements " "common-refpoint-of-elements " "pure-relevant-elements " + "skyline-spacing " "cached-pure-extents " ); diff --git a/lily/beam.cc b/lily/beam.cc index dcbed75ce5..0778c58388 100644 --- a/lily/beam.cc +++ b/lily/beam.cc @@ -296,6 +296,10 @@ typedef map > Position_stem_segments_map; vector Beam::get_beam_segments (Grob *me_grob, Grob **common) { + /* ugh, this has a side-effect that we need to ensure that + Stem #'beaming is correct */ + (void) me_grob->get_property ("quantized-positions"); + Spanner *me = dynamic_cast (me_grob); extract_grob_set (me, "stems", stems); diff --git a/lily/grob.cc b/lily/grob.cc index 28f665138c..1456a264ef 100644 --- a/lily/grob.cc +++ b/lily/grob.cc @@ -621,6 +621,7 @@ ADD_INTERFACE (Grob, "grob-interface", "meta " "minimum-X-extent " "minimum-Y-extent " + "outside-staff-priority " "rotation " "springs-and-rods " "staff-symbol " diff --git a/lily/include/axis-group-interface.hh b/lily/include/axis-group-interface.hh index 767271ad34..b1bd975454 100644 --- a/lily/include/axis-group-interface.hh +++ b/lily/include/axis-group-interface.hh @@ -30,6 +30,7 @@ struct Axis_group_interface static Interval cached_pure_height (Grob *me, vector const &list, Grob *common, int, int); + static void skyline_spacing (Grob *me, vector elements); static void add_element (Grob *me, Grob *); static void set_axes (Grob *, Axis, Axis); static bool has_axis (Grob *, Axis); diff --git a/lily/include/grob.hh b/lily/include/grob.hh index 5eb433921a..64fb7322a6 100644 --- a/lily/include/grob.hh +++ b/lily/include/grob.hh @@ -9,6 +9,7 @@ #ifndef GROB_HH #define GROB_HH +#include "box.hh" #include "virtual-methods.hh" #include "dimension-cache.hh" #include "grob-interface.hh" diff --git a/lily/include/lily-guile.hh b/lily/include/lily-guile.hh index 3422089e9c..0f94e2ebbb 100644 --- a/lily/include/lily-guile.hh +++ b/lily/include/lily-guile.hh @@ -114,9 +114,7 @@ inline SCM ly_append4 (SCM x1, SCM x2, SCM x3, SCM x4) /* display and print newline. */ -extern "C" { - void ly_display_scm (SCM s); -} +void ly_display_scm (void *s); void read_lily_scm_file (string); void ly_c_init_guile (); diff --git a/lily/include/skyline.hh b/lily/include/skyline.hh index fc8d3acedd..c8440c7f94 100644 --- a/lily/include/skyline.hh +++ b/lily/include/skyline.hh @@ -1,41 +1,64 @@ /* - skyline.hh -- declare Skyline_entry and funcbs. + skyline.hh -- declare Skyline class. source file of the GNU LilyPond music typesetter - (c) 2002--2006 Han-Wen Nienhuys + (c) 2006 Joe Neeman */ #ifndef SKYLINE_HH #define SKYLINE_HH -#include "std-vector.hh" +#include +#include "axis.hh" #include "box.hh" +#include "interval.hh" +#include "direction.hh" +#include "std-vector.hh" +#include "stencil.hh" -struct Skyline_entry +struct Building { - Interval width_; - Real height_; - Skyline_entry (); - Skyline_entry (Interval, Real); + Interval iv_; + Real start_height_; + Real end_height_; + Real m_; + Real b_; + + Building (Real start, Real start_height, Real end_height, Real end, Real max_slope); + + Real height (Real x) const; + Real intersection (Building const &other) const; + void leading_part (Real chop, Real h); + bool obstructs (Building const &other) const; }; -void -merge_skyline (vector *a1, vector const &a2, - Direction); -void insert_extent_into_skyline (vector *line, Box b, Axis line_axis, - Direction d); -vector -extents_to_skyline (vector const &extents, Axis a, Direction d); -vector empty_skyline (Direction d); -void heighten_skyline (vector *buildings, Real ground); -Real -skyline_meshing_distance (vector const &buildings, - vector const &clouds); - -Real -skyline_height (vector const &buildings, - Real airplane, Direction sky_dir); +class Skyline +{ +private: + list buildings_; + Direction sky_; + Real max_slope_; + void internal_merge_skyline (list*, list*, + list *const result); + void internal_build_skyline (list*, + list *const result); + bool is_legal_skyline () const; + +public: + Skyline (); + Skyline (Direction sky); + Skyline (vector const &bldgs, Axis a, Direction sky); + + void merge (Skyline const &); + void insert (Box const &, Axis); + void raise (Real); + Real distance (Skyline const &) const; + Real height (Real airplane) const; + Real max_height () const; + void set_minimum_height (Real height); + Stencil stencil (); +}; #endif /* SKYLINE_HH */ diff --git a/lily/include/system.hh b/lily/include/system.hh index 2fe740fddb..f10b9b750f 100644 --- a/lily/include/system.hh +++ b/lily/include/system.hh @@ -11,6 +11,7 @@ #include "column-x-positions.hh" #include "spanner.hh" #include "grob-array.hh" +#include "skyline.hh" /* If you keep following offset reference points, you will always end @@ -21,6 +22,8 @@ class System : public Spanner { int rank_; Grob_array *all_elements_; + Drul_array skylines_; + void build_skylines (); void init_elements (); friend class Paper_score; // ugh. Paper_score *pscore_; // ugh. diff --git a/lily/include/tie-column-format.hh b/lily/include/tie-column-format.hh index 1c82c4269e..3206e3203f 100644 --- a/lily/include/tie-column-format.hh +++ b/lily/include/tie-column-format.hh @@ -13,7 +13,7 @@ #include "lily-proto.hh" #include "tie-configuration.hh" -void set_chord_outline (vector *skyline, +void set_chord_outline (Skyline *skyline, vector bounds, Grob *common, Direction d); @@ -25,7 +25,7 @@ void final_shape_adjustment (Tie_configuration &conf, Tie_formatting_problem const&, Grob *staff_referencer); void -set_chord_outlines (Drul_array< vector > *skyline_drul, +set_chord_outlines (Drul_array *skyline_drul, vector ties, Grob *common); diff --git a/lily/include/tie-formatting-problem.hh b/lily/include/tie-formatting-problem.hh index 5d46129fe5..7d48995d60 100644 --- a/lily/include/tie-formatting-problem.hh +++ b/lily/include/tie-formatting-problem.hh @@ -47,7 +47,7 @@ struct Tie_configuration_variation Tie_configuration_variation (); }; -typedef map < Tuple, vector > Chord_outline_map; +typedef map < Tuple, Skyline> Chord_outline_map; typedef map < Tuple, Box> Column_extent_map; class Tie_formatting_problem { diff --git a/lily/instrument-name-engraver.cc b/lily/instrument-name-engraver.cc index 377d66b832..9290ef93e5 100644 --- a/lily/instrument-name-engraver.cc +++ b/lily/instrument-name-engraver.cc @@ -91,6 +91,15 @@ Instrument_name_engraver::start_spanner () text_spanner_->set_bound (LEFT, col); text_spanner_->set_property ("text", short_text_); text_spanner_->set_property ("long-text", long_text_); + + /* + UGH, should handle this in Score_engraver. + */ + Grob *system = unsmob_grob (get_property ("rootSystem")); + if (system) + Axis_group_interface::add_element (system, text_spanner_); + else + text_spanner_->programming_error ("can't find root system"); } @@ -130,16 +139,6 @@ Instrument_name_engraver::stop_spanner () Pointer_group_interface::set_ordered (text_spanner_, ly_symbol2scm ("elements"), false); - System *system = get_root_system (text_spanner_); - - /* - UGH, should handle this in Score_engraver. - */ - if (system) - Axis_group_interface::add_element (system, text_spanner_); - else - text_spanner_->programming_error ("can't find root system"); - text_spanner_ = 0; } diff --git a/lily/lily-guile.cc b/lily/lily-guile.cc index e1a0e9bffd..e932f342b7 100644 --- a/lily/lily-guile.cc +++ b/lily/lily-guile.cc @@ -114,15 +114,12 @@ gulp_file_to_string (string fn, bool must_exist, int size) return result; } -extern "C" { - // maybe gdb 5.0 becomes quicker if it doesn't do fancy C++ typing? - void - ly_display_scm (SCM s) - { - scm_display (s, scm_current_output_port ()); - scm_newline (scm_current_output_port ()); - } -}; +void +ly_display_scm (void *s) +{ + scm_display ((SCM)s, scm_current_output_port ()); + scm_newline (scm_current_output_port ()); +} string ly_scm2string (SCM str) diff --git a/lily/main.cc b/lily/main.cc index 4e72121d12..2eb6cec0ba 100644 --- a/lily/main.cc +++ b/lily/main.cc @@ -641,3 +641,25 @@ main (int argc, char **argv) /* Only reachable if GUILE exits. That is an error. */ return 1; } + +SCM atexit_list = SCM_EOL; + +LY_DEFINE (ly_atexit, "ly:atexit", + 2, 0, 0, (SCM proc, SCM args), + "Just before exiting, call the procedure given. " +"If this is called multiple times, the procedures are called " +"in LIFO order.") +{ + atexit_list = scm_cons (scm_cons (proc, args), atexit_list); + scm_gc_protect_object (atexit_list); + return SCM_UNSPECIFIED; +} + +LY_DEFINE (ly_do_atexit, "ly:do-atexit", + 0, 0, 0, (), + "Call the atexit procedures.") +{ + for (SCM s = atexit_list; scm_is_pair (s); s = scm_cdr (s)) + scm_apply_0 (scm_caar (s), scm_cdar (s)); + return SCM_UNSPECIFIED; +} diff --git a/lily/program-option.cc b/lily/program-option.cc index be81de380b..02213e4c71 100644 --- a/lily/program-option.cc +++ b/lily/program-option.cc @@ -21,6 +21,7 @@ using namespace std; /* Write midi as formatted ascii stream? */ bool do_midi_debugging_global; bool use_object_keys; +bool debug_skylines; /* Backwards compatibility. @@ -89,6 +90,11 @@ void internal_set_option (SCM var, SCM val) strict_infinity_checking = to_boolean (val); val = scm_from_bool (to_boolean (val)); } + else if (var == ly_symbol2scm ("debug-skylines")) + { + debug_skylines = to_boolean (val); + val = scm_from_bool (to_boolean (val)); + } } ssize const HELP_INDENT = 30; diff --git a/lily/skyline.cc b/lily/skyline.cc index f72ea5b2f6..e52a6277d5 100644 --- a/lily/skyline.cc +++ b/lily/skyline.cc @@ -1,202 +1,364 @@ -/* - skyline.cc -- implement Skyline_entry and funcs. +/* skyline.cc -- implement the Skyline class - source file of the GNU LilyPond music typesetter - - (c) 2002--2006 Han-Wen Nienhuys + source file of the GNU LilyPond music typesetter + + (c) 2006 Joe Neeman */ #include "skyline.hh" -/* - A skyline is a shape of the form: - - - * ---- - * | | - * ---------| | - * | | - * | | - * | |______ - * --------| |___ - * - - This file deals with building such skyline structure, and computing - the minimum distance between two opposing skylines. - - Invariants for a skyline: - - skyline[...].width_ forms a partition of the real interval, where - the segments are adjacent, and ascending. Hence we have - - skyline.back ().width_[RIGHT] = inf - skyline[0].width_[LEFT] = -inf +#include "line-interface.hh" + +/* A skyline is a sequence of non-overlapping buildings: something like + this: + _______ + / \ ________ + / \ ________/ \ + /\ / \ / \ + / -----/ \ / \ + / \ / \ + / ------------/ ---- + -- + Each building has a starting position, and ending position, a starting + height and an ending height. + + The following invariants are observed: + - the start of the first building is at -infinity + - the end of the last building is at infinity + - if a building has infinite length (ie. the first and last buildings), + then its starting height and ending height are equal + - the end of one building is the same as the beginning of the next + building + + We also allow skylines to point down (the structure is exactly the same, + but we think of the part above the line as being filled with mass and the + part below as being empty). ::distance finds the minimum distance between + an UP skyline and a DOWN skyline. + + Note that we store DOWN skylines upside-down. That is, in order to compare + a DOWN skyline with an UP skyline, we need to flip the DOWN skyline first. + This means that the merging routine doesn't need to be aware of direction, + but the distance routine does. */ -const Real EPS = 1e-12; - -/* - TODO: avoid unnecessary fragmentation. +#define EPS 1e-10 - This is O (n^2), searching and insertion. Could be O (n log n) with - binsearch. -*/ -void -insert_extent_into_skyline (vector *line, Box b, Axis line_axis, - Direction d) +static inline bool +equal (Real x, Real y) { - Interval extent = b[line_axis]; - if (extent.is_empty ()) - return; + return abs (x - y) < EPS || (isinf (x) && isinf (y) && ((x > 0) == (y > 0))); +} - Real stick_out = b[other_axis (line_axis)][d]; +bool +Skyline::is_legal_skyline () const +{ + list::const_iterator i; + Real last_x = -infinity_f; + Real last_h = -infinity_f; + for (i = buildings_.begin (); i != buildings_.end (); i++) + { + if (i->iv_[LEFT] != last_x) + return false; + if (i != buildings_.begin () && !equal (i->start_height_, last_h)) + return false; + last_x = i->iv_[RIGHT]; + last_h = i->end_height_; + } + return last_x == infinity_f; +} - /* - Intersect each segment of LINE with EXTENT, and if non-empty, insert relevant segments. - */ - for (vsize i = line->size (); i--;) +Building::Building (Real start, Real start_height, Real end_height, Real end, Real max_slope) + : iv_ (start, end) +{ + start_height_ = start_height; + end_height_ = end_height; + + if (isinf (start)) + assert (isinf (start_height) || start_height == end_height); + if (isinf (end)) + assert (isinf (end_height) || start_height == end_height); + + m_ = (end_height - start_height) / (end - start); + if (start_height == end_height) + m_ = 0; + if (isinf (m_) || isnan (m_)) + m_ = max_slope * (start_height < end_height ? 1 : -1); + assert (abs (m_) <= max_slope); + + if (isinf (start)) { - Interval w = line->at (i).width_; - w.intersect (extent); + if (isinf (end)) + b_ = start_height; + else + b_ = end_height - m_*end; + } + else + b_ = start_height - m_*start; +} - if (extent[LEFT] >= w[RIGHT]) - break; +Real +Building::height (Real x) const +{ + if (isinf (x)) + return (x > 0) ? end_height_ : start_height_; + return m_*x + b_; +} - Real my_height = line->at (i).height_; +Real +Building::intersection (Building const &other) const +{ + return (b_ - other.b_) / (other.m_ - m_); +} - if (!w.is_empty () - && w.length () > EPS - && d * (my_height - stick_out) < 0) - { - Interval e1 (line->at (i).width_[LEFT], extent[LEFT]); - Interval e3 (extent[RIGHT], line->at (i).width_[RIGHT]); +void +Building::leading_part (Real chop, Real h) +{ + assert (chop > iv_[LEFT] && chop <= iv_[RIGHT] && !equal (chop, iv_[LEFT])); + assert (equal (h, height (chop))); + iv_[RIGHT] = chop; + end_height_ = h; +} - if (!e3.is_empty () && e3.length () > EPS) - line->insert (line->begin () + i + 1, Skyline_entry (e3, my_height)); +static void +skyline_trailing_part (list *sky, Real x) +{ + if (equal (x, sky->front ().iv_[RIGHT])) + sky->pop_front (); + else + assert (x < sky->front ().iv_[RIGHT]); - line->at (i).height_ = stick_out; - line->at (i).width_ = w; - if (!e1.is_empty () && e1.length () > EPS) - line->insert (line->begin () + i, Skyline_entry (e1, my_height)); - } + if (!sky->empty ()) + { + sky->front ().iv_[LEFT] = x; + sky->front ().start_height_ = sky->front ().height (x); } } +bool +Building::obstructs (Building const &other) const +{ + if (equal (intersection (other), iv_[LEFT]) || equal (start_height_, other.start_height_)) + return m_ > other.m_ || (m_ == other.m_ && b_ > other.b_); + return start_height_ > other.start_height_; +} + void -merge_skyline (vector *a1, - vector const &a2, - Direction dir) +Skyline::internal_merge_skyline (list *s1, list *s2, + list *const result) { - for (vsize i = 0; i < a2.size (); i++) + while (!s1->empty ()) { - Box b; - b[X_AXIS] = a2[i].width_; - b[Y_AXIS][dir] = a2[i].height_; - b[Y_AXIS][-dir] = dir * infinity_f; - - insert_extent_into_skyline (a1, b, X_AXIS, dir); + if (s2->front ().obstructs (s1->front ())) + swap (s1, s2); + + Building b = s1->front (); + while (s2->front ().iv_[RIGHT] < b.iv_[RIGHT] + && s2->front ().end_height_ <= b.height (s2->front ().iv_[RIGHT]) + EPS) + s2->pop_front (); + + /* the front of s2 either intersects with b or it ends after b */ + Real end = infinity_f; + Real s2_end_height = s2->front ().end_height_; + Real s1_end_height = b.height (s2->front ().iv_[RIGHT]); + if (s2_end_height > s1_end_height + EPS) + end = b.intersection (s2->front ()); + end = min (end, b.iv_[RIGHT]); + Real height = b.height (end); + + b.leading_part (end, height); + result->push_front (b); + + skyline_trailing_part (s1, end); + if (!s1->empty ()) + s1->front ().start_height_ = height; + skyline_trailing_part (s2, end); } + result->reverse (); } -vector -empty_skyline (Direction d) +static void +empty_skyline (list *const ret) { - vector skyline; + ret->push_front (Building (-infinity_f, -infinity_f, -infinity_f, infinity_f, 0)); +} - Interval i; - i.set_empty (); - i.swap (); - Skyline_entry e; - e.width_ = i; - e.height_ = -d * infinity_f; - skyline.push_back (e); - return skyline; +static void +single_skyline (Building const &b, list *const ret, Real max_slope) +{ + if (!isinf (b.iv_[RIGHT])) + ret->push_front (Building (b.iv_[RIGHT], b.end_height_, -infinity_f, infinity_f, max_slope)); + ret->push_front (b); + if (!isinf (b.iv_[LEFT])) + ret->push_front (Building (-infinity_f, -infinity_f, b.start_height_, b.iv_[LEFT], max_slope)); } -vector -extents_to_skyline (vector const &extents, Axis a, Direction d) +void +Skyline::internal_build_skyline (list *buildings, list *const result) { + vsize size = buildings->size (); - vector skyline = empty_skyline (d); + if (size == 0) + { + empty_skyline (result); + return; + } + else if (size == 1) + { + single_skyline (buildings->front (), result, max_slope_); + return; + } - /* - This makes a cubic algorithm (array insertion is O (n), - searching the array dumbly is O (n), and for n items, we get O (n^3).) + list right_half; + list::iterator i = buildings->begin (); - We could do a lot better (n log (n), using a balanced tree) but - that seems overkill for now. - */ - for (vsize j = extents.size (); j--;) - insert_extent_into_skyline (&skyline, extents[j], a, d); + for (vsize s = 0; s < size/2; s++) + i++; + right_half.splice (right_half.end (), *buildings, i, buildings->end ()); - return skyline; + list right; + list left; + internal_build_skyline (&right_half, &right); + internal_build_skyline (buildings, &left); + internal_merge_skyline (&right, &left, result); } -/* - minimum distance that can be achieved between baselines. "Clouds" is - a skyline pointing down. +Skyline::Skyline () +{ + max_slope_ = 2; + sky_ = UP; + empty_skyline (&buildings_); +} - This is an O (n) algorithm. -*/ -Real -skyline_meshing_distance (vector const &buildings, - vector const &clouds) +Skyline::Skyline (Direction sky) { - int i = buildings.size () -1; - int j = clouds.size () -1; + max_slope_ = 2; + sky_ = sky; + empty_skyline (&buildings_); +} - Real distance = -infinity_f; +Skyline::Skyline (vector const &boxes, Axis a, Direction sky) +{ + list bldgs; + sky_ = sky; + max_slope_ = 2; - while (i > 0 || j > 0) + for (vsize i = 0; i < boxes.size (); i++) { - Interval w = buildings[i].width_; - w.intersect (clouds[j].width_); + Interval iv = boxes[i][a]; + Real height = sky * boxes[i][other_axis (a)][sky]; + if (!iv.is_empty () && !isinf (height) && !equal (iv[LEFT], iv[RIGHT])) + bldgs.push_front (Building (iv[LEFT], height, height, iv[RIGHT], max_slope_)); + } + internal_build_skyline (&bldgs, &buildings_); + assert (is_legal_skyline ()); +} - if (!w.is_empty ()) - distance = max (distance, (buildings[i].height_ - clouds[j].height_)); +void +Skyline::merge (Skyline const &other) +{ + assert (sky_ == other.sky_); - if (i > 0 && buildings[i].width_[LEFT] >= clouds[j].width_[LEFT]) - i--; - else if (j > 0 && buildings[i].width_[LEFT] <= clouds[j].width_[LEFT]) - j--; - } + list other_bld (other.buildings_); + list my_bld; + my_bld.splice (my_bld.begin (), buildings_); + internal_merge_skyline (&other_bld, &my_bld, &buildings_); + assert (is_legal_skyline ()); +} - return distance; +void +Skyline::insert (Box const &b, Axis a) +{ + list other_bld; + list my_bld; + Interval iv = b[a]; + Real height = sky_ * b[other_axis (a)][sky_]; + + my_bld.splice (my_bld.begin (), buildings_); + single_skyline (Building (iv[LEFT], height, height, iv[RIGHT], max_slope_), &other_bld, max_slope_); + internal_merge_skyline (&other_bld, &my_bld, &buildings_); + assert (is_legal_skyline ()); } -Skyline_entry::Skyline_entry () +void +Skyline::raise (Real r) { - height_ = 0.0; + list::iterator end = buildings_.end (); + for (list::iterator i = buildings_.begin (); i != end; i++) + { + i->start_height_ += sky_ * r; + i->end_height_ += sky_ * r; + i->b_ += sky_ * r; + } + assert (is_legal_skyline ()); } -Skyline_entry::Skyline_entry (Interval i, Real r) +Real +Skyline::distance (Skyline const &other) const { - width_ = i; - height_ = r; + assert (sky_ == -other.sky_); + list::const_iterator i = buildings_.begin (); + list::const_iterator j = other.buildings_.begin (); + + Real dist = -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]) + i++; + else + j++; + } + return dist; } -void -heighten_skyline (vector *buildings, Real ground) +Real +Skyline::height (Real airplane) const { - for (vsize i = 0; i < buildings->size (); i++) - buildings->at (i).height_ += ground; + assert (!isinf (airplane)); + + list::const_iterator i; + for (i = buildings_.begin (); i != buildings_.end (); i++) + { + if (i->iv_[RIGHT] >= airplane) + return sky_ * i->height (airplane); + } + assert (0); + return 0; } Real -skyline_height (vector const &buildings, - Real airplane, - Direction sky_dir) +Skyline::max_height () const { - Real h = - sky_dir * infinity_f; + Skyline s (-sky_); + s.set_minimum_height (0); + return sky_ * distance (s); +} - /* - Ugh! linear, should be O(log n). - */ - for (vsize i = 0; i < buildings.size (); i++) - if (buildings[i].width_.contains (airplane)) - h = sky_dir * max (sky_dir * h, - sky_dir * buildings[i].height_); - - return h; +void +Skyline::set_minimum_height (Real h) +{ + Skyline s (sky_); + s.buildings_.front ().start_height_ = h*sky_; + s.buildings_.front ().end_height_ = h*sky_; + s.buildings_.front ().b_ = h*sky_; + merge (s); } +Stencil +Skyline::stencil () +{ + Stencil ret; + for (list::iterator i = buildings_.begin (); i != buildings_.end (); i++) + { + if (!isinf (i->iv_.length ())) + { + Stencil line = Line_interface::make_line (0.1, + Offset (i->iv_[LEFT], sky_*i->start_height_), + Offset (i->iv_[RIGHT], sky_*i->end_height_)); + ret.add_stencil (line); + } + } + return ret; +} diff --git a/lily/system.cc b/lily/system.cc index 0649849b21..7c1828cabd 100644 --- a/lily/system.cc +++ b/lily/system.cc @@ -24,6 +24,8 @@ #include "tweak-registration.hh" #include "warn.hh" +extern bool debug_skylines; + System::System (System const &src, int count) : Spanner (src, count) { @@ -186,6 +188,13 @@ System::get_paper_systems () System *system = dynamic_cast (broken_intos_[i]); system->post_processing (); + system->build_skylines (); + if (i > 0) + { + System *prev = dynamic_cast (broken_intos_[i-1]); + Real r = prev->skylines_[DOWN].distance (system->skylines_[UP]); + system->set_property ("skyline-distance", scm_from_double (r)); + } scm_vector_set_x (lines, scm_from_int (i), system->get_paper_system ()); @@ -391,6 +400,11 @@ System::get_paper_system () Stencil sys_stencil (Box (x, y), scm_cons (ly_symbol2scm ("combine-stencil"), exprs)); + if (debug_skylines) + { + sys_stencil.add_stencil (skylines_[UP].stencil ().in_color (255, 0, 0)); + sys_stencil.add_stencil (skylines_[DOWN].stencil ().in_color (0, 255, 0)); + } Grob *left_bound = this->get_bound (LEFT); SCM prop_init = left_bound->get_property ("line-break-system-details"); @@ -399,6 +413,7 @@ System::get_paper_system () /* information that the page breaker might need */ Grob *right_bound = this->get_bound (RIGHT); + pl->set_property ("skyline-distance", get_property ("skyline-distance")); 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")); @@ -499,6 +514,25 @@ 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)); + } + + skylines_[UP] = Skyline (boxes, X_AXIS, UP); + skylines_[DOWN] = Skyline (boxes, X_AXIS, DOWN); +} ADD_INTERFACE (System, "system-interface", @@ -510,4 +544,5 @@ ADD_INTERFACE (System, "system-interface", "columns " "pure-Y-extent " "spaceable-staves " + "skyline-distance " ) diff --git a/lily/tie-formatting-problem.cc b/lily/tie-formatting-problem.cc index 4dada25d22..0e29d3ca50 100644 --- a/lily/tie-formatting-problem.cc +++ b/lily/tie-formatting-problem.cc @@ -51,7 +51,7 @@ Tie_formatting_problem::get_attachment (Real y, Drul_array columns) const if (i == chord_outlines_.end ()) programming_error ("Can't find chord outline"); else - attachments[d] = skyline_height ((*i).second, y, -d); + attachments[d] = i->second.height (y); } while (flip (&d) != LEFT); @@ -115,28 +115,6 @@ Tie_formatting_problem::set_column_chord_outline (vector bounds, } Tuple2 key (column_rank, int (dir)); - - chord_outlines_[key] = empty_skyline (-dir); - - if (bounds[0]->break_status_dir ()) - { - Real x = robust_relative_extent (bounds[0], x_refpoint_, X_AXIS)[-dir]; - chord_outlines_[key].at (0).height_ = x; - } - else - { - Interval x; - for (vsize j = 0; j < head_boxes.size (); j++) - { - x.unite (head_boxes[j][X_AXIS]); - } - - chord_outlines_[key].at (0).height_ = x[dir]; - } - - for (vsize i = 0; i < boxes.size (); i++) - insert_extent_into_skyline (&chord_outlines_[key] , - boxes[i], Y_AXIS, -dir); if (stem && !Stem::is_invisible (stem)) @@ -150,8 +128,8 @@ Tie_formatting_problem::set_column_chord_outline (vector bounds, Direction stemdir = get_grob_direction (stem); y.add_point (Stem::head_positions (stem)[-stemdir] * staff_space * .5); - - insert_extent_into_skyline (&chord_outlines_[key], Box (x,y), Y_AXIS, -dir); + + boxes.push_back (Box (x, y)); stem_extents_[key].unite (Box (x,y)); @@ -159,8 +137,7 @@ Tie_formatting_problem::set_column_chord_outline (vector bounds, { Box flag_box = Stem::get_translated_flag (stem).extent_box (); flag_box.translate( Offset (x[RIGHT], X_AXIS)); - insert_extent_into_skyline (&chord_outlines_[key], flag_box, - Y_AXIS, -dir); + boxes.push_back (flag_box); } } else if (stem) @@ -176,10 +153,8 @@ Tie_formatting_problem::set_column_chord_outline (vector bounds, Interval y_ext; for (vsize j = 0; j < head_boxes.size (); j++) y_ext.unite (head_boxes[j][Y_AXIS]); - - insert_extent_into_skyline (&chord_outlines_[key], - Box (x_ext, y_ext), - Y_AXIS, -dir); + + boxes.push_back (Box (x_ext, y_ext)); } Direction updowndir = DOWN; @@ -197,12 +172,27 @@ Tie_formatting_problem::set_column_chord_outline (vector bounds, } if (!x.is_empty ()) - insert_extent_into_skyline (&chord_outlines_[key], - Box (x,y), - Y_AXIS, -dir); + boxes.push_back (Box (x, y)); } while (flip (&updowndir) != DOWN); + chord_outlines_[key] = Skyline (boxes, Y_AXIS, -dir); + if (bounds[0]->break_status_dir ()) + { + Real x = robust_relative_extent (bounds[0], x_refpoint_, X_AXIS)[-dir]; + chord_outlines_[key].set_minimum_height (x); + } + else + { + Interval x; + for (vsize j = 0; j < head_boxes.size (); j++) + { + x.unite (head_boxes[j][X_AXIS]); + } + + chord_outlines_[key].set_minimum_height (x[dir]); + } + head_extents_[key].set_empty (); for (vsize i = 0; i < head_boxes.size (); i++) { @@ -344,22 +334,12 @@ Tie_formatting_problem::from_semi_ties (vector const &semi_ties, Directio set_chord_outline (heads, head_dir); - Real extremal = head_dir * infinity_f; - Tuple2 head_key (column_rank, head_dir); Tuple2 open_key (column_rank, -head_dir); - - for (vsize i = 0; i < chord_outlines_[head_key].size (); i++) - { - extremal = head_dir * min (head_dir * extremal, - head_dir * chord_outlines_[head_key][i].height_); - } + Real extremal = chord_outlines_[head_key].max_height (); - Skyline_entry right_entry; - right_entry.width_.set_full (); - right_entry.height_ = extremal - head_dir * 1.5; - - chord_outlines_[open_key].push_back (right_entry); + chord_outlines_[open_key] = Skyline (head_dir); + chord_outlines_[open_key].set_minimum_height (extremal - head_dir * 1.5); } diff --git a/scm/define-grob-properties.scm b/scm/define-grob-properties.scm index dddcef9aee..38fe501282 100644 --- a/scm/define-grob-properties.scm +++ b/scm/define-grob-properties.scm @@ -274,6 +274,9 @@ get stems extending to the middle staff line.") (non-musical ,boolean? "True if the grob belongs in a NonMusicalPaperColumn.") (number-type ,symbol? "Type of numbers to use in label. Choices include @code{roman-lower}, @code{roman-upper}, and @code{arabic}.") + (outside-staff-priority ,number? "When set, the grob will be positioned outside the staff +in such a way as to avoid all collisions. In case of a potential collision, the grob with +the smaller outside-staff-priority will be closer to the staff.") (packed-spacing ,boolean? "If set, the notes are spaced as tightly as possible.") (padding ,ly:dimension? "Add this much extra space between @@ -549,6 +552,9 @@ than a whole rest.") (spaceable-staves ,ly:grob-array? "Objects to be spaced during page layout.") + (skyline-distance ,number? "The distance between this staff and the next one, as determined by a skyline algorithm.") + (skyline-spacing ,boolean? "When true, this axis-group will vertically space its children +using a skyline algorithm.") ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; diff --git a/scm/define-grobs.scm b/scm/define-grobs.scm index bd5dee7706..5e2dd4d86c 100644 --- a/scm/define-grobs.scm +++ b/scm/define-grobs.scm @@ -1643,6 +1643,7 @@ (axes . (0 1)) (X-extent . ,ly:axis-group-interface::width) (Y-extent . ,ly:axis-group-interface::height) + (skyline-spacing . #t) (meta . ((class . System) (interfaces . (system-interface axis-group-interface)))))) @@ -1967,6 +1968,7 @@ (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) + (skyline-spacing . #t) (meta . ((class . Spanner) (interfaces . (axis-group-interface hara-kiri-group-interface diff --git a/scm/graphviz.scm b/scm/graphviz.scm new file mode 100644 index 0000000000..d3a858fcef --- /dev/null +++ b/scm/graphviz.scm @@ -0,0 +1,35 @@ +(define-module (scm graphviz) + #:use-module (lily) + #:export (make-graph add-node add-edge)) + +(define (make-graph filename) + (let ((empty-graph (list->vector (list filename '() '() '())))) + (ly:atexit write-graph (list empty-graph)) + empty-graph)) + +(define (filename g) (vector-ref g 0)) +(define (nodes g) (vector-ref g 1)) +(define (edges g) (vector-ref g 2)) +(define (clusters g) (vector-ref g 3)) + +(define (add-node graph label) + (let ((ns (nodes graph))) + (vector-set! graph 1 (cons `(,(length ns) . ,label) ns)) + (length ns))) + +(define (add-edge graph node1 node2) + (vector-set! graph 2 (cons `(,node1 . ,node2) (edges graph)))) + +(define (write-graph graph) + (let ((out (open-file (filename graph) "w")) + (ns (nodes graph)) + (es (edges graph)) + (cc (clusters graph))) + (ly:message (format "writing graph ~s..." (filename graph))) + (display "digraph G {\nrankdir=\"LR\"\nnode [shape=rectangle]\n" out) + (map (lambda (n) (display (format "~a [label=\"~a\"]\n" (car n) (cdr n)) out)) + ns) + (map (lambda (e) (display (format "~a -> ~a\n" (car e) (cdr e)) out)) + es) + (display "}" out))) + \ No newline at end of file diff --git a/scm/layout-page-layout.scm b/scm/layout-page-layout.scm index e370a17132..090ad06b7b 100644 --- a/scm/layout-page-layout.scm +++ b/scm/layout-page-layout.scm @@ -73,9 +73,10 @@ "Minimum distance between `line' reference position and `next-line' reference position. If next-line is #f, return #f." (and next-line - (max 0 (- (+ (interval-end (paper-system-extent next-line Y)) - (if ignore-padding 0 (line-next-padding line next-line layout))) - (interval-start (paper-system-extent line Y)))))) + (let ((non-skyline-distance (- (interval-end (paper-system-extent next-line Y)) + (interval-start (paper-system-extent line Y))))) + (max 0 (+ (ly:prob-property next-line 'skyline-distance non-skyline-distance) + (if ignore-padding 0 (line-next-padding line next-line layout))))))) (define (line-ideal-distance line next-line layout ignore-padding) "Ideal distance between `line' reference position and `next-line' diff --git a/scm/lily.scm b/scm/lily.scm index 2241c85047..f3767ca9f5 100644 --- a/scm/lily.scm +++ b/scm/lily.scm @@ -26,6 +26,7 @@ (debug-lexer #f "debug the flex lexer") (debug-midi #f "generate human readable MIDI") (debug-parser #f "debug the bison parser") + (debug-skylines #f "debug skylines") (delete-intermediate-files #f "delete unusable PostScript files") (dump-signatures #f "dump output signatures of each system") @@ -421,6 +422,7 @@ The syntax is the same as `define*-public'." (ly:error (_ "failed files: ~S") (string-join failed)) (exit 1)) (begin + (ly:do-atexit) ;; HACK: be sure to exit with single newline (ly:message "") (exit 0)))))