From ac65bf686db14f9032da28c60c523e109707a12e Mon Sep 17 00:00:00 2001 From: Han-Wen Nienhuys Date: Tue, 13 Dec 2005 15:54:20 +0000 Subject: [PATCH] * Documentation/topdocs/NEWS.tely (Top): document new feature. * lily/tie-formatting-problem.cc (score_ties): new function (get_variations): new function. Try flipping dirs for collisions. (generate_optimal_chord_configuration): new function. 1-opt search for better configuration. * lily/tie-configuration.cc: new file. * lily/tie.cc: junk Tie::get_configuration() * lily/tie-formatting-problem.cc (score_ties_aptitude): new function. (score_ties_configuration): new function. (generate_ties_configuration): new function. (generate_base_chord_configuration): new function. (set_ties_config_standard_directions): Move body from tie-column-format.cc (set_manual_tie_configuration): Move body from tie-column-format.cc * lily/tie-formatting-problem.cc (score_configuration): score tie/dot collisions. * lily/tie-helper.cc (get_transformed_bezier): new function * Documentation/topdocs/NEWS.tely (Top): strip out-www. --- ChangeLog | 18 ++ Documentation/topdocs/NEWS.tely | 14 ++ lily/include/lily-proto.hh | 1 - lily/include/tie-column-format.hh | 5 +- lily/include/tie-configuration.hh | 48 ++++ lily/include/tie-formatting-problem.hh | 46 +++- lily/include/tie.hh | 37 +-- lily/laissez-vibrer-tie-column.cc | 50 +--- lily/tie-column-format.cc | 78 +----- lily/tie-column.cc | 62 +---- lily/tie-configuration.cc | 97 ++++++++ lily/tie-formatting-problem.cc | 316 ++++++++++++++++++++++++- lily/tie-helper.cc | 87 ------- lily/tie.cc | 206 +--------------- 14 files changed, 557 insertions(+), 508 deletions(-) create mode 100644 lily/include/tie-configuration.hh create mode 100644 lily/tie-configuration.cc diff --git a/ChangeLog b/ChangeLog index bf2afb8259..9e27e68e49 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,23 @@ 2005-12-13 Han-Wen Nienhuys + * Documentation/topdocs/NEWS.tely (Top): document new feature. + + * lily/tie-formatting-problem.cc (score_ties): new function + (get_variations): new function. Try flipping dirs for collisions. + (generate_optimal_chord_configuration): new function. 1-opt search + for better configuration. + + * lily/tie-configuration.cc: new file. + + * lily/tie.cc: junk Tie::get_configuration() + + * lily/tie-formatting-problem.cc (score_ties_aptitude): new function. + (score_ties_configuration): new function. + (generate_ties_configuration): new function. + (generate_base_chord_configuration): new function. + (set_ties_config_standard_directions): Move body from tie-column-format.cc + (set_manual_tie_configuration): Move body from tie-column-format.cc + * input/regression/tie-dot.ly: new file. * lily/tie-formatting-problem.cc (score_configuration): score diff --git a/Documentation/topdocs/NEWS.tely b/Documentation/topdocs/NEWS.tely index 050f8889ee..a2e06d516b 100644 --- a/Documentation/topdocs/NEWS.tely +++ b/Documentation/topdocs/NEWS.tely @@ -45,6 +45,20 @@ This document is also available in @uref{NEWS.pdf,PDF}. @itemize @bullet + +@item Ties in chords are also formatted using a scoring based +formatting. This reduces the number of collisions for ties in +chords, + +@lilypond[raggedright,fragment,relative=2] + ~ +@end lilypond + +Here, the tie for the F is flipped, in spite the default rule for +directions in chords. + +This rewrite was sponsored by Steve Doonan. + @item With the @code{\tweak} music function, layout objects that are directly connected to input may be tuned easily, diff --git a/lily/include/lily-proto.hh b/lily/include/lily-proto.hh index b0f413fb78..d7ea27aa7c 100644 --- a/lily/include/lily-proto.hh +++ b/lily/include/lily-proto.hh @@ -161,7 +161,6 @@ class Tie; class Tie_details; class Tie_configuration; class Tie_formatting_problem; -class Ties_configuration; class Tie_performer; class Time_scaled_music; class Time_scaled_music_iterator; diff --git a/lily/include/tie-column-format.hh b/lily/include/tie-column-format.hh index dfdb5aa07d..ffa43c9ad0 100644 --- a/lily/include/tie-column-format.hh +++ b/lily/include/tie-column-format.hh @@ -1,5 +1,5 @@ /* - tie-column-format.hh -- declare + tie-column-format.hh -- declare Tie column format routines. source file of the GNU LilyPond music typesetter @@ -10,7 +10,8 @@ #ifndef TIE_COLUMN_FORMAT_HH #define TIE_COLUMN_FORMAT_HH - +#include "lily-proto.hh" +#include "tie-configuration.hh" void set_chord_outline (Array *skyline, Link_array bounds, diff --git a/lily/include/tie-configuration.hh b/lily/include/tie-configuration.hh new file mode 100644 index 0000000000..f3add86fd5 --- /dev/null +++ b/lily/include/tie-configuration.hh @@ -0,0 +1,48 @@ +/* + tie-configuration.hh -- declare Tie_configuration + + source file of the GNU LilyPond music typesetter + + (c) 2005 Han-Wen Nienhuys + +*/ + +#ifndef TIE_CONFIGURATION_HH +#define TIE_CONFIGURATION_HH + +#include "lily-proto.hh" +#include "direction.hh" +#include "interval.hh" +#include "compare.hh" +#include "array.hh" + +class Tie_configuration +{ +public: + int position_; + Direction dir_; + Real delta_y_; + + /* computed. */ + Interval attachment_x_; + + Tie_configuration (); + void center_tie_vertically (Tie_details const &); + Bezier get_transformed_bezier (Tie_details const &) const; + Bezier get_untransformed_bezier (Tie_details const &) const; + Real height (Tie_details const&) const; + + static int compare (Tie_configuration const &a, + Tie_configuration const &b); + static Real distance (Tie_configuration const &a, + Tie_configuration const &b); +}; + +INSTANTIATE_COMPARE (Tie_configuration, Tie_configuration::compare); + +typedef Array Ties_configuration; + +#endif /* TIE_CONFIGURATION_HH */ + + + diff --git a/lily/include/tie-formatting-problem.hh b/lily/include/tie-formatting-problem.hh index 36afc52ff6..34e0d10bb0 100644 --- a/lily/include/tie-formatting-problem.hh +++ b/lily/include/tie-formatting-problem.hh @@ -14,6 +14,7 @@ #include "parray.hh" #include "skyline.hh" #include "lily-proto.hh" +#include "tie-configuration.hh" #include #include @@ -34,22 +35,50 @@ struct Tie_details typedef map< pair, Tie_configuration *> Tie_configuration_map; +struct Tie_specification +{ + int position_; + + bool has_manual_position_; + bool has_manual_dir_; + + Real manual_position_; + Direction manual_dir_; + + Tie_specification (); +}; + +struct Tie_configuration_variation +{ + int index_; + Tie_configuration *suggestion_; + Tie_configuration_variation (); +}; + class Tie_formatting_problem { Drul_array< Array > chord_outlines_; set dot_positions_; Interval dot_x_; + Array specifications_; Tie_configuration_map possibilities_; - Tie_configuration *get_configuration (int position, Direction dir); - Tie_configuration *generate_configuration (int position, Direction dir); - Real score_configuration (Tie_configuration const&); - Real score_aptitude (Tie_configuration const&, int); - Grob *x_refpoint_; + Tie_configuration *get_configuration (int position, Direction dir); + Tie_configuration *generate_configuration (int position, Direction dir) const; + Array get_variations (Ties_configuration const &ties); + + Real score_configuration (Tie_configuration const&) const; + Real score_aptitude (Tie_configuration const&, int) const; + Real score_ties_aptitude (Ties_configuration const &ties) const; + Real score_ties_configuration (Ties_configuration const &ties) const; + void set_ties_config_standard_directions (Ties_configuration *tie_configs_ptr); + Real score_ties (Ties_configuration const&) const; + Ties_configuration generate_base_chord_configuration (); + public: Tie_details details_; @@ -57,12 +86,15 @@ public: Tie_formatting_problem (); ~Tie_formatting_problem (); - Tie_configuration find_optimal_tie_configuration (int p, Direction d); + + Ties_configuration generate_optimal_chord_configuration (); + Ties_configuration generate_ties_configuration (Ties_configuration const &); + Tie_configuration find_optimal_tie_configuration (int p, Direction d) const; void from_ties (Link_array const &ties); void from_tie (Grob *tie); void from_lv_ties (Link_array const &); void set_chord_outline (Link_array, Direction); - + void set_manual_tie_configuration (SCM); Interval get_attachment (Real) const; Grob *common_x_refpoint () const; }; diff --git a/lily/include/tie.hh b/lily/include/tie.hh index d78e5e1b14..dc69e9c6a3 100644 --- a/lily/include/tie.hh +++ b/lily/include/tie.hh @@ -15,38 +15,6 @@ -class Tie_configuration -{ -public: - int position_; - Direction dir_; - Real delta_y_; - - - /* computed. */ - Interval attachment_x_; - Grob *tie_; - int head_position_; - - Tie_configuration (); - void center_tie_vertically (Tie_details const &); - Bezier get_transformed_bezier (Tie_details const &) const; - Bezier get_untransformed_bezier (Tie_details const &) const; - Real height (Tie_details const&) const; - - static int compare (Tie_configuration const &a, - Tie_configuration const &b); - static Real distance (Tie_configuration const &a, - Tie_configuration const &b); -}; - -INSTANTIATE_COMPARE (Tie_configuration, Tie_configuration::compare); - -class Ties_configuration -{ -public: - Array ties_; -}; class Tie { @@ -57,8 +25,12 @@ public: static int get_column_rank (Grob *, Direction); static int get_position (Grob *); static Direction get_default_dir (Grob *); + +#if 0 static void get_configuration (Grob *, Tie_configuration *, Tie_formatting_problem const &); +#endif + static void set_control_points (Grob *, Grob *, Tie_configuration const&, Tie_details const&); @@ -73,7 +45,6 @@ public: static Interval get_default_attachments (Spanner *me, Grob *common, Real gap, int *staff_position, bool *in_between, Tie_details const &); - }; diff --git a/lily/laissez-vibrer-tie-column.cc b/lily/laissez-vibrer-tie-column.cc index a65de78ccc..55e510a91e 100644 --- a/lily/laissez-vibrer-tie-column.cc +++ b/lily/laissez-vibrer-tie-column.cc @@ -16,8 +16,9 @@ #include "pointer-group-interface.hh" #include "staff-symbol-referencer.hh" #include "item.hh" -#include "tie-column-format.hh" #include "tie-formatting-problem.hh" +#include "tie-configuration.hh" +#include "tie-column-format.hh" ADD_INTERFACE(Laissez_vibrer_tie_column, @@ -46,52 +47,21 @@ Laissez_vibrer_tie_column::calc_positioning_done (SCM smob) lv_ties.sort (&Laissez_vibrer_tie::compare); Ties_configuration ties_config; - for (int i = 0; i < lv_ties.size (); i++) - { - Tie_configuration conf; - conf.dir_ = CENTER; - Item *head = unsmob_item (lv_ties[i]->get_object ("note-head")); - - if (head) - conf.position_ = (int) Staff_symbol_referencer::get_position (head); - - ties_config.ties_.push (conf); - } - - bool manual_override = false; - SCM manual_configs = me->get_property ("tie-configuration"); - set_manual_tie_configuration (&ties_config, - &manual_override, - manual_configs - ); - - set_tie_config_directions (&ties_config); + Tie_formatting_problem problem; + problem.from_lv_ties (lv_ties); - /* - Calculate final width and shape of the ties. - */ - for (int i = 0; i < lv_ties.size(); i++) - { - final_shape_adjustment (ties_config.ties_[i], - problem, lv_ties[0]); - } - - /* - Try to shift small ties into available spaces. - */ - if (!manual_override) - { - shift_small_ties (&ties_config, lv_ties[0], problem.details_); - } - + SCM manual_configs = me->get_property ("tie-configuration"); + problem.set_manual_tie_configuration (manual_configs); + + Ties_configuration base = problem.generate_optimal_chord_configuration (); for (int i = 0; i < lv_ties.size(); i++) { - Tie::set_control_points (lv_ties[i], problem.common_x_refpoint (), ties_config.ties_[i], + Tie::set_control_points (lv_ties[i], problem.common_x_refpoint (), base[i], problem.details_); - set_grob_direction (lv_ties[i], ties_config.ties_[i].dir_); + set_grob_direction (lv_ties[i], base[i].dir_); } return SCM_BOOL_T; diff --git a/lily/tie-column-format.cc b/lily/tie-column-format.cc index 1c6a3cb362..91e65a3448 100644 --- a/lily/tie-column-format.cc +++ b/lily/tie-column-format.cc @@ -17,37 +17,10 @@ #include "directional-element-interface.hh" #include "rhythmic-head.hh" #include "tie-formatting-problem.hh" +#include "tie-configuration.hh" #include -void -set_manual_tie_configuration (Ties_configuration *ties_config, - bool *manual_override, - SCM manual_configs - ) -{ - *manual_override = false; - int k = 0; - for (SCM s = manual_configs; - scm_is_pair (s) && k < ties_config->ties_.size(); s = scm_cdr (s)) - { - SCM entry = scm_car (s); - if (!scm_is_pair (entry)) - continue; - - *manual_override = true; - Tie_configuration &conf = ties_config->ties_.elem_ref (k); - - Real complete_pos = robust_scm2double (scm_car (entry), - conf.position_); - - conf.position_ = int (rint (complete_pos)); - conf.delta_y_ = complete_pos - conf.position_; - conf.dir_ = Direction (robust_scm2int (scm_cdr (entry), - conf.dir_)); - k ++; - } -} void shift_small_ties (Ties_configuration *tie_configs, @@ -55,12 +28,12 @@ shift_small_ties (Ties_configuration *tie_configs, Tie_details const &details) { set positions_taken; - for (int i = 0; i < tie_configs->ties_.size (); i++) - positions_taken.insert (int (rint (tie_configs->ties_.elem (i).position_))); + for (int i = 0; i < tie_configs->size (); i++) + positions_taken.insert (int (rint (tie_configs->elem (i).position_))); - for (int i = 0; i < tie_configs->ties_.size (); i++) + for (int i = 0; i < tie_configs->size (); i++) { - Tie_configuration * conf = &tie_configs->ties_.elem_ref (i); + Tie_configuration * conf = &tie_configs->elem_ref (i); /* on staff line and small enough, translate a little further @@ -113,45 +86,4 @@ final_shape_adjustment (Tie_configuration &conf, conf.center_tie_vertically (details); } -void -set_tie_config_directions (Ties_configuration *tie_configs_ptr) -{ - Array &tie_configs (tie_configs_ptr->ties_); - - if (!tie_configs[0].dir_) - tie_configs[0].dir_ = DOWN; - if (!tie_configs.top().dir_) - tie_configs.top().dir_ = UP; - - /* - Seconds - */ - for (int i = 1; i < tie_configs.size(); i++) - { - Real diff = tie_configs[i-1].position_ - - tie_configs[i].position_; - - if (fabs (diff) <= 1) - { - if (!tie_configs[i-1].dir_) - tie_configs[i-1].dir_ = DOWN; - if (!tie_configs[i].dir_) - tie_configs[i].dir_ = UP; - } - } - - for (int i = 1; i < tie_configs.size() - 1; i++) - { - Tie_configuration &conf = tie_configs.elem_ref (i); - if (conf.dir_) - continue; - - Direction position_dir = - Direction (sign (conf.position_)); - if (!position_dir) - position_dir = DOWN; - - conf.dir_ = position_dir; - } -} diff --git a/lily/tie-column.cc b/lily/tie-column.cc index 31b51592dc..51bce9bc90 100644 --- a/lily/tie-column.cc +++ b/lily/tie-column.cc @@ -19,6 +19,7 @@ #include "directional-element-interface.hh" #include "tie-column-format.hh" #include "tie-formatting-problem.hh" +#include "tie-configuration.hh" using namespace std; @@ -83,67 +84,22 @@ Tie_column::calc_positioning_done (SCM smob) ties.sort (&Tie::compare); - Ties_configuration ties_config; - for (int i = 0; i < ties.size (); i++) - { - Tie_configuration conf; - if (scm_is_number (ties[i]->get_property_data (ly_symbol2scm ("direction")))) - conf.dir_ = get_grob_direction (ties[i]); - conf.position_ = Tie::get_position (ties[i]); - ties_config.ties_.push (conf); - } - - SCM manual_configs = me->get_property ("tie-configuration"); - bool manual_override = false; - set_manual_tie_configuration (&ties_config, - &manual_override, - manual_configs); - set_tie_config_directions (&ties_config); - Tie_formatting_problem problem; problem.from_ties (ties); - - /* - Let the ties flow out, according to our single-tie formatting. - */ - if (!manual_override) - { - Tie::get_configuration (ties[0], &ties_config.ties_.elem_ref (0), - problem); - Tie::get_configuration (ties.top (), - &ties_config.ties_.elem_ref (ties_config.ties_.size()-1), - problem); - } - /* - Calculate final width and shape of the ties. - */ - for (int i = 0; i < ties.size(); i++) - { - if (!manual_override - && (i == 0 || i == ties.size () -1)) - continue; + SCM manual_configs = me->get_property ("tie-configuration"); + problem.set_manual_tie_configuration (manual_configs); - final_shape_adjustment (ties_config.ties_[i], - problem, - ties[0]); - } + Ties_configuration base = problem.generate_optimal_chord_configuration (); - - /* - Try to shift small ties into available spaces. - */ - if (!manual_override) - { - shift_small_ties (&ties_config, ties[0], problem.details_); - } - - for (int i = 0; i < ties.size(); i++) + for (int i = 0; i < base.size(); i++) { - Tie::set_control_points (ties[i], problem.common_x_refpoint (), ties_config.ties_[i], + Tie::set_control_points (ties[i], problem.common_x_refpoint (), + base[i], problem.details_); - set_grob_direction (ties[i], ties_config.ties_[i].dir_); + set_grob_direction (ties[i], + base[i].dir_); } return SCM_BOOL_T; } diff --git a/lily/tie-configuration.cc b/lily/tie-configuration.cc new file mode 100644 index 0000000000..39483b90f4 --- /dev/null +++ b/lily/tie-configuration.cc @@ -0,0 +1,97 @@ +/* + tie-configuration.cc -- implement Tie_configuration + + source file of the GNU LilyPond music typesetter + + (c) 2005 Han-Wen Nienhuys + +*/ + +#include "tie-configuration.hh" +#include "warn.hh" +#include "tie-formatting-problem.hh" +#include "bezier.hh" + +int +Tie_configuration::compare (Tie_configuration const &a, + Tie_configuration const &b) +{ + if (a.position_ - b.position_) + return sign (a.position_ - b.position_); + return sign (a.dir_ - b.dir_); +} + + +Tie_configuration::Tie_configuration () +{ + dir_ = CENTER; + position_ = 0; + delta_y_ = 0.0; +} + + +void +Tie_configuration::center_tie_vertically (Tie_details const &details) +{ + Bezier b = get_untransformed_bezier (details); + Offset middle = b.curve_point (0.5); + Offset edge = b.curve_point (0.0); + Real center = (edge[Y_AXIS] + middle[Y_AXIS])/2.0; + + delta_y_ = - dir_ * center; +} + + +/* + Get bezier with left control at (0,0) + */ +Bezier +Tie_configuration::get_transformed_bezier (Tie_details const &details) const +{ + Bezier b (get_untransformed_bezier (details)); + + b.scale (1, dir_); + b.translate (Offset (attachment_x_[LEFT], + delta_y_ + details.staff_space_ * 0.5 * position_)); + + return b; +} + +/* + Get bezier with left control at (0,0) + */ +Bezier +Tie_configuration::get_untransformed_bezier (Tie_details const &details) const +{ + Real l = attachment_x_.length(); + if (isnan (l) || isnan (l)) + { + programming_error ("Inf or NaN encountered"); + l = 1.0; + } + return slur_shape (l, + details.height_limit_, + details.ratio_); +} + +Real +Tie_configuration::distance (Tie_configuration const &a, + Tie_configuration const &b) +{ + + Real d = 3 * (a.position_ - b.position_); + if (d < 0) + return d + (2 + (b.dir_ - a.dir_)); + else + return d + (2 + (a.dir_ - b.dir_)); +} + +Real +Tie_configuration::height (Tie_details const &details) const +{ + Real l = attachment_x_.length(); + + return slur_shape (l, + details.height_limit_, + details.ratio_).curve_point (0.5)[Y_AXIS]; +} diff --git a/lily/tie-formatting-problem.cc b/lily/tie-formatting-problem.cc index 2fd6b6063b..ae23935241 100644 --- a/lily/tie-formatting-problem.cc +++ b/lily/tie-formatting-problem.cc @@ -9,16 +9,17 @@ #include "tie-formatting-problem.hh" +#include "tie-configuration.hh" #include "directional-element-interface.hh" #include "staff-symbol-referencer.hh" #include "tie.hh" - #include "item.hh" #include "spanner.hh" #include "bezier.hh" #include "stem.hh" #include "note-head.hh" #include "rhythmic-head.hh" +#include "warn.hh" Interval Tie_formatting_problem::get_attachment (Real y) const @@ -195,6 +196,22 @@ Tie_formatting_problem::from_ties (Link_array const &ties) set_chord_outline (bounds, d); } while (flip (&d) != LEFT); + + + for (int i = 0; i < ties.size (); i++) + { + Tie_specification spec; + + if (scm_is_number (ties[i]->get_property_data (ly_symbol2scm ("direction")))) + { + spec.manual_dir_ = to_dir (ties[i]->get_property ("direction")); + spec.has_manual_dir_ = true; + } + + spec.position_ = Tie::get_position (ties[i]); + + specifications_.push (spec); + } } void @@ -205,11 +222,19 @@ Tie_formatting_problem::from_lv_ties (Link_array const &lv_ties) details_.from_grob (lv_ties[0]); Link_array heads; + for (int i = 0; i < lv_ties.size (); i++) { + Tie_specification spec; Item *head = unsmob_item (lv_ties[i]->get_object ("note-head")); + if (!head) - continue; + programming_error ("LV tie without head?!"); + + if (head) + { + spec.position_ = int (Staff_symbol_referencer::get_position (head)); + } heads.push (head); } @@ -237,7 +262,7 @@ Tie_formatting_problem::from_lv_ties (Link_array const &lv_ties) } Tie_configuration* -Tie_formatting_problem::get_configuration (int pos, Direction dir) +Tie_formatting_problem::get_configuration (int pos, Direction dir) { pair key (pos, dir); Tie_configuration_map::const_iterator f = possibilities_.find (key); @@ -254,7 +279,7 @@ Tie_formatting_problem::get_configuration (int pos, Direction dir) } Tie_configuration* -Tie_formatting_problem::generate_configuration (int pos, Direction dir) +Tie_formatting_problem::generate_configuration (int pos, Direction dir) const { Tie_configuration *conf = new Tie_configuration; conf->position_ = pos; @@ -289,7 +314,7 @@ Tie_formatting_problem::generate_configuration (int pos, Direction dir) Real Tie_formatting_problem::score_aptitude (Tie_configuration const &conf, - int tie_position) + int tie_position) const { Real wrong_direction_offset_penalty_; Real distance_penalty_factor_; @@ -309,7 +334,7 @@ Tie_formatting_problem::score_aptitude (Tie_configuration const &conf, Real -Tie_formatting_problem::score_configuration (Tie_configuration const &conf) +Tie_formatting_problem::score_configuration (Tie_configuration const &conf) const { Real length_penalty_factor = 1.0; Real min_length = 0.333; @@ -372,7 +397,7 @@ Tie_formatting_problem::score_configuration (Tie_configuration const &conf) } Tie_configuration -Tie_formatting_problem::find_optimal_tie_configuration (int pos, Direction dir) +Tie_formatting_problem::find_optimal_tie_configuration (int pos, Direction dir) const { Link_array confs; @@ -405,3 +430,280 @@ Tie_formatting_problem::find_optimal_tie_configuration (int pos, Direction dir) return best; } + +Tie_specification::Tie_specification () +{ + has_manual_position_ = false; + has_manual_dir_ = false; + position_ = 0; + manual_position_ = 0; + manual_dir_ = CENTER; +} + + +Real +Tie_formatting_problem::score_ties_aptitude (Ties_configuration const &ties) const +{ + Real score = 0.0; + if (ties.size () != specifications_.size ()) + { + programming_error ("Huh? Mismatch between sizes."); + return infinity_f; + } + + for (int i = 0; i < ties.size (); i++) + score += score_aptitude (ties[i], specifications_[i].position_); + + return score; +} + +Real +Tie_formatting_problem::score_ties (Ties_configuration const &ties) const +{ + return score_ties_configuration (ties) + + score_ties_aptitude (ties); +} + +Real +Tie_formatting_problem::score_ties_configuration (Ties_configuration const &ties) const +{ + const Real tie_monotonicity_penalty = 100; + const Real tie_collision_penalty = 30; + const Real tie_collision_distance = 0.25; + + Real score = 0.0; + for (int i = 0; i < ties.size (); i++) + { + score += score_configuration (ties[i]); + } + + + Real last_edge = 0.0; + Real last_center = 0.0; + for (int i = 0; i < ties.size (); i++) + { + Bezier b (ties[i].get_transformed_bezier (details_)); + + Real center = b.curve_point (0.5)[Y_AXIS]; + Real edge = b.curve_point (0.0)[Y_AXIS]; + + if (i) + { + if (edge <= last_edge) + score += tie_monotonicity_penalty; + if (center <= last_center) + score += tie_monotonicity_penalty; + + score += + tie_collision_penalty + * max (tie_collision_distance - fabs (center - last_center), 0.0) / tie_collision_distance; + score += + tie_collision_penalty + * max (tie_collision_distance - fabs (edge - last_edge), 0.0) / tie_collision_distance; + } + + last_edge = edge; + last_center = center; + } + + return score; +} + +/* + Generate with correct X-attachments and beziers, copying delta_y_ + from TIES_CONFIG if necessary. +*/ +Ties_configuration +Tie_formatting_problem::generate_ties_configuration (Ties_configuration const &ties_config) +{ + Ties_configuration copy; + for (int i = 0; i < ties_config.size (); i++) + { + Tie_configuration * ptr = get_configuration (ties_config[i].position_, ties_config[i].dir_); + if (specifications_[i].has_manual_position_) + ptr->delta_y_ = specifications_[i].manual_position_; + + copy.push (*ptr); + } + + return copy; +} + +Ties_configuration +Tie_formatting_problem::generate_base_chord_configuration () +{ + Ties_configuration ties_config; + for (int i = 0; i < specifications_.size (); i ++) + { + Tie_configuration conf; + if (specifications_[i].has_manual_dir_) + conf.dir_ = specifications_[i].manual_dir_; + if (specifications_[i].has_manual_position_) + { + conf.position_ = (int) round (specifications_[i].manual_position_); + conf.delta_y_ = (specifications_[i].manual_position_ - conf.position_) + * 0.5 * details_.staff_space_; + } + else + conf.position_ = specifications_[i].position_; + + ties_config.push (conf); + } + + set_ties_config_standard_directions (&ties_config); + + ties_config = generate_ties_configuration (ties_config); + + return ties_config; +} + +Ties_configuration +Tie_formatting_problem::generate_optimal_chord_configuration () +{ + Ties_configuration base = generate_base_chord_configuration (); + Array vars = get_variations (base); + + Ties_configuration best = base; + Real best_score = score_ties_configuration (best); + + /* + This simply is 1-opt: we have K substitions, and we try applying + exactly every one for each. + */ + for (int i = 0; i < vars.size (); i++) + { + Ties_configuration variant = base; + variant[vars[i].index_] = *vars[i].suggestion_; + + Real score = (score_ties_configuration (variant) + + score_ties_aptitude (variant)); + if (score < best_score) + { + best = variant; + best_score = score; + } + } + + return best; +} + +void +Tie_formatting_problem::set_ties_config_standard_directions (Ties_configuration *tie_configs) +{ + if (!tie_configs->elem (0).dir_) + tie_configs->elem_ref (0).dir_ = DOWN; + if (!tie_configs->top().dir_) + tie_configs->top().dir_ = UP; + + /* + Seconds + */ + for (int i = 1; i < tie_configs->size (); i++) + { + Real diff = (tie_configs->elem (i-1).position_ + - tie_configs->elem (i).position_); + + if (fabs (diff) <= 1) + { + if (!tie_configs->elem (i-1).dir_) + tie_configs->elem_ref (i-1).dir_ = DOWN; + if (!tie_configs->elem (i).dir_) + tie_configs->elem_ref (i).dir_ = UP; + } + } + + for (int i = 1; i < tie_configs->size() - 1; i++) + { + Tie_configuration &conf = tie_configs->elem_ref (i); + if (conf.dir_) + continue; + + Direction position_dir = + Direction (sign (conf.position_)); + if (!position_dir) + position_dir = DOWN; + + conf.dir_ = position_dir; + } +} + +Tie_configuration_variation::Tie_configuration_variation () +{ + index_ = 0; + suggestion_ = 0; +} + +Array +Tie_formatting_problem::get_variations (Ties_configuration const &ties) +{ + Real center_distance_tolerance = 0.25; + + Array vars; + Real last_center = 0.0; + for (int i = 0; i < ties.size (); i++) + { + Bezier b (ties[i].get_transformed_bezier (details_)); + + Real center = b.curve_point (0.5)[Y_AXIS]; + + if (i) + { + if (center <= last_center + center_distance_tolerance) + { + if (!specifications_[i].has_manual_dir_) + { + Tie_configuration_variation var; + var.index_ = i; + var.suggestion_ = get_configuration (ties[i].position_, + -ties[i].dir_); + + vars.push (var); + } + + if (!specifications_[i-1].has_manual_dir_) + { + Tie_configuration_variation var; + var.index_ = i-1; + var.suggestion_ = get_configuration (ties[i-1].position_, + -ties[i-1].dir_); + + vars.push (var); + } + } + } + + last_center = center; + } + + return vars; + +} + +void +Tie_formatting_problem::set_manual_tie_configuration (SCM manual_configs) +{ + int k = 0; + for (SCM s = manual_configs; + scm_is_pair (s) && k < specifications_.size(); s = scm_cdr (s)) + { + SCM entry = scm_car (s); + if (!scm_is_pair (entry)) + continue; + + Tie_specification &spec = specifications_[k]; + + if (scm_is_number (scm_cdr (entry))) + { + spec.has_manual_dir_ = true; + spec.manual_dir_ = Direction (scm_to_int (scm_cdr (entry))); + } + if (scm_is_number (scm_car (entry))) + { + spec.has_manual_position_ = true; + spec.manual_position_ = scm_to_double (scm_cdr (entry)); + } + + k ++; + } +} + diff --git a/lily/tie-helper.cc b/lily/tie-helper.cc index b0ec652d2b..454276b42b 100644 --- a/lily/tie-helper.cc +++ b/lily/tie-helper.cc @@ -14,92 +14,6 @@ #include "warn.hh" #include "tie-formatting-problem.hh" -int -Tie_configuration::compare (Tie_configuration const &a, - Tie_configuration const &b) -{ - if (a.position_ - b.position_) - return sign (a.position_ - b.position_); - return sign (a.dir_ - b.dir_); -} - - -Tie_configuration::Tie_configuration () -{ - dir_ = CENTER; - position_ = 0; - delta_y_ = 0.0; -} - - -void -Tie_configuration::center_tie_vertically (Tie_details const &details) -{ - Bezier b = get_untransformed_bezier (details); - Offset middle = b.curve_point (0.5); - Offset edge = b.curve_point (0.0); - Real center = (edge[Y_AXIS] + middle[Y_AXIS])/2.0; - - delta_y_ = - dir_ * center; -} - - -/* - Get bezier with left control at (0,0) - */ -Bezier -Tie_configuration::get_transformed_bezier (Tie_details const &details) const -{ - Bezier b (get_untransformed_bezier (details)); - - b.scale (1, dir_); - b.translate (Offset (attachment_x_[LEFT], - delta_y_ + details.staff_space_ * 0.5 * position_)); - - return b; -} - -/* - Get bezier with left control at (0,0) - */ -Bezier -Tie_configuration::get_untransformed_bezier (Tie_details const &details) const -{ - Real l = attachment_x_.length(); - if (isnan (l) || isnan (l)) - { - programming_error ("Inf or NaN encountered"); - l = 1.0; - } - return slur_shape (l, - details.height_limit_, - details.ratio_); -} - -Real -Tie_configuration::distance (Tie_configuration const &a, - Tie_configuration const &b) -{ - - Real d = 3 * (a.position_ - b.position_); - if (d < 0) - return d + (2 + (b.dir_ - a.dir_)); - else - return d + (2 + (a.dir_ - b.dir_)); -} - -Real -Tie_configuration::height (Tie_details const &details) const -{ - Real l = attachment_x_.length(); - - return slur_shape (l, - details.height_limit_, - details.ratio_).curve_point (0.5)[Y_AXIS]; -} - - - void Tie_details::from_grob (Grob *me) { @@ -107,7 +21,6 @@ Tie_details::from_grob (Grob *me) staff_space_ = Staff_symbol_referencer::staff_space (me); SCM details = me->get_property ("details"); - height_limit_ = robust_scm2double (ly_assoc_get (ly_symbol2scm ("height-limit"), details, SCM_EOL), 0.75) * staff_space_; diff --git a/lily/tie.cc b/lily/tie.cc index 2b743a30ca..188e1df3be 100644 --- a/lily/tie.cc +++ b/lily/tie.cc @@ -22,6 +22,7 @@ #include "tie-column.hh" #include "grob-array.hh" #include "tie-formatting-problem.hh" +#include "tie-configuration.hh" int @@ -193,211 +194,6 @@ Tie::get_default_attachments (Spanner *me, Grob *common, Real gap, return attachments; } -void -Tie::get_configuration (Grob *me_grob, - Tie_configuration *conf, - Tie_formatting_problem const &problem) -{ - Spanner *me = dynamic_cast (me_grob); - if (!head (me, LEFT) && !head (me, RIGHT)) - { - programming_error ("tie without heads"); - me->suicide (); - return ; - } - - /* - UGH. Don't mirror Tie_configuration. - */ - conf->head_position_ = (int) Tie::get_position (me); - if (!conf->dir_) - conf->dir_ = get_grob_direction (me); - if (!conf->dir_) - conf->dir_ = get_default_dir (me); - - Real staff_space = problem.details_.staff_space_; - bool in_between = true; - Real gap = robust_scm2double (me->get_property ("x-gap"), 0.2); - - if (conf->attachment_x_.is_empty()) - { -#if 0 - if (!skylines) - conf->attachment_x_ = get_default_attachments (me, common, gap, - &conf->position_, - &in_between, problem.details_); -#endif - Real y = staff_space * 0.5 * conf->position_; - conf->attachment_x_ = problem.get_attachment (y); - conf->attachment_x_.widen (-gap); - } - - Bezier b = slur_shape (conf->attachment_x_.length(), - problem.details_.height_limit_, - problem.details_.ratio_); - b.scale (1, conf->dir_); - - Offset middle = b.curve_point (0.5); - Offset edge = b.curve_point (0.0); - - conf->position_ = int (rint (conf->position_)); - - Real dy = fabs (middle[Y_AXIS] - edge[Y_AXIS]); - bool in_space = !(Staff_symbol_referencer::on_staffline (me, (int) conf->position_)); - bool fits_in_space = - (dy < 0.6 * staff_space); - - /* - Avoid dot - */ - Grob *left_dot = unsmob_grob (me->get_bound (LEFT)->get_object ("dot")); - int dot_pos = left_dot - ? int (Staff_symbol_referencer::get_position (left_dot)) - : 0; - if (left_dot - && (conf->position_ == dot_pos - || conf->position_ + conf->dir_ == dot_pos)) - { - conf->position_ += conf->dir_; - in_space = !in_space; - - if (conf->position_ == dot_pos) - { - conf->position_ += conf->dir_; - in_space = !in_space; - } - - Real y = staff_space * 0.5 * conf->position_; - conf->attachment_x_ = problem.get_attachment (y); - conf->attachment_x_.widen (-gap); - Bezier b = slur_shape (conf->attachment_x_.length(), - problem.details_.height_limit_, - problem.details_.ratio_); - Offset middle = b.curve_point (0.5); - Offset edge = b.curve_point (0.0); - dy = fabs (middle[Y_AXIS] - edge[Y_AXIS]); - fits_in_space = - (dy < 0.6 * staff_space); - } - - /* - Avoid flag. - */ - Grob *left_stem = unsmob_grob (me->get_bound (LEFT)->get_object ("stem")); - if (left_stem) - { - Stencil flag = Stem::get_translated_flag (left_stem); - Real y = conf->position_ * staff_space * 0.5; - if (flag.extent (Y_AXIS).contains (y)) - { - conf->position_ += conf->dir_; - in_space = !in_space; - } - } - - if (in_space != fits_in_space) - { - if (in_space) - { - conf->position_ += conf->dir_; - } - else - { - in_space = true; - conf->position_ += conf->dir_; - } - - /* - ugh: code dup. - */ - Real y = staff_space * 0.5 * conf->position_; - conf->attachment_x_ = problem.get_attachment (y); - conf->attachment_x_.widen (-gap); - - Bezier b = slur_shape (conf->attachment_x_.length(), - problem.details_.height_limit_, - problem.details_.ratio_); - Offset middle = b.curve_point (0.5); - Offset edge = b.curve_point (0.0); - dy = fabs (middle[Y_AXIS] - edge[Y_AXIS]); - fits_in_space = - (dy < 0.6 * staff_space); - } - - - /* - Putting larger in-space ties next to the notes forces - the edges to be opposite (Y-wise) to the tie direction. - */ - if (conf->position_ == conf->head_position_ - && in_space - && Staff_symbol_referencer::staff_radius (me) > abs (conf->position_) / 2 - && dy > 0.3 * staff_space - ) - { - conf->position_ += 2 * conf->dir_; - } - - if (!in_between - && in_space - && abs (conf->position_ - conf->head_position_) <= 1) - { - int amount = conf->dir_; - if (sign (conf->position_) != conf->dir_ - || conf->position_ < Staff_symbol_referencer::staff_radius (me) * 2) - amount *= 2; - - conf->position_ += amount; - } - - if (in_space) - { - if ((sign (conf->position_) != conf->dir_ - || conf->position_ < Staff_symbol_referencer::staff_radius (me) * 2) - && - ((abs (conf->position_ - conf->head_position_) <= 1 - && fabs (dy) < 0.45 * staff_space) - || fabs (dy) < 0.6 * staff_space)) - { - /* - vertically center in space. - */ - conf->center_tie_vertically (problem.details_); - } - else - { - conf->delta_y_ = - conf->dir_ * staff_space * (- 0.3); - } - } - else - { - Real where = 0.5 * conf->dir_; - - Real rounding_dy = (where - middle[Y_AXIS]); - conf->delta_y_ = rounding_dy; - - if (conf->dir_ * (b.curve_point (0.0)[Y_AXIS] - + conf->position_ * staff_space * 0.5 - + conf->delta_y_) < - conf->dir_ * conf->head_position_ * 0.5 * staff_space) - { - if (Staff_symbol_referencer::staff_radius (me) > abs (conf->head_position_) / 2) - conf->position_ += 2 * conf->dir_; - else - conf->position_ += conf->dir_; - } - } - - - Real half_space = 0.5 * staff_space; - Real y = conf->position_ * half_space; - - conf->attachment_x_ = problem.get_attachment (y); - conf->attachment_x_.widen (-gap); -} - - void Tie::set_default_control_points (Grob *me_grob) { -- 2.39.5