diff options
author | Han-Wen Nienhuys <hanwen@xs4all.nl> | 2007-01-12 22:01:09 +0100 |
---|---|---|
committer | Han-Wen Nienhuys <hanwen@xs4all.nl> | 2007-01-12 22:01:09 +0100 |
commit | e832bb9391d6053f7328250ff1035376cf4b9d4b (patch) | |
tree | 7993d3296627877db28a579c3b146a6b0a2749a2 | |
parent | 3032ee2728314075c2cf98ef7f9f0d42008fb707 (diff) |
Fix #138 & #139
- use chord formatting for single tie
- add all notes + accidentals to chord outline
- separate file for Tie_specification
- use convex_amplifier for distance penalties:
penalties increase superlinearly in distance
- tunable padding for skyline
- semi tie: set direction with ^ and _
- debug output for semities
- for single tie case, introduce penalty for wrong direction
- only add symmetry penalties for more than 1 tie.
- use exactness to determine whether to override delta_y_ too.
-rw-r--r-- | input/regression/semi-tie-manual-direction.ly | 16 | ||||
-rw-r--r-- | input/regression/tie-accidental.ly | 19 | ||||
-rw-r--r-- | input/regression/tie-manual-vertical-tune.ly | 23 | ||||
-rw-r--r-- | input/regression/tie-single-chord.ly | 17 | ||||
-rw-r--r-- | lily/include/misc.hh | 1 | ||||
-rw-r--r-- | lily/include/tie-configuration.hh | 1 | ||||
-rw-r--r-- | lily/include/tie-details.hh | 2 | ||||
-rw-r--r-- | lily/include/tie-formatting-problem.hh | 33 | ||||
-rw-r--r-- | lily/include/tie-specification.hh | 36 | ||||
-rw-r--r-- | lily/laissez-vibrer-engraver.cc | 12 | ||||
-rw-r--r-- | lily/misc.cc | 8 | ||||
-rw-r--r-- | lily/repeat-tie-engraver.cc | 13 | ||||
-rw-r--r-- | lily/semi-tie-column.cc | 6 | ||||
-rw-r--r-- | lily/tie-column.cc | 29 | ||||
-rw-r--r-- | lily/tie-configuration.cc | 28 | ||||
-rw-r--r-- | lily/tie-details.cc | 2 | ||||
-rw-r--r-- | lily/tie-formatting-problem.cc | 401 | ||||
-rw-r--r-- | lily/tie-specification.cc | 47 | ||||
-rw-r--r-- | lily/tie.cc | 33 | ||||
-rw-r--r-- | scm/define-grobs.scm | 3 |
20 files changed, 489 insertions, 241 deletions
diff --git a/input/regression/semi-tie-manual-direction.ly b/input/regression/semi-tie-manual-direction.ly new file mode 100644 index 0000000000..bfa44106df --- /dev/null +++ b/input/regression/semi-tie-manual-direction.ly @@ -0,0 +1,16 @@ + +\header { + texidoc = "Semi tie directions may be forced from the input." +} + +\version "2.10.0" + +\layout{ + ragged-right=##t +} + +{ + r4 + c'^\laissezVibrer^\repeatTie + r4 +} diff --git a/input/regression/tie-accidental.ly b/input/regression/tie-accidental.ly new file mode 100644 index 0000000000..2f1ec24218 --- /dev/null +++ b/input/regression/tie-accidental.ly @@ -0,0 +1,19 @@ +\header { texidoc = " + lilypond should flip the tie's direction + to avoid a collision with the sharp. +" } + + +\version "2.11.11" + +\paper { + debug-tie-scoring = ##t + ragged-right = ##t +} + +\relative c'' { + \override Tie #'layer = #2 + a ~ <fis a> +} + +% EOF diff --git a/input/regression/tie-manual-vertical-tune.ly b/input/regression/tie-manual-vertical-tune.ly new file mode 100644 index 0000000000..ed631d9a01 --- /dev/null +++ b/input/regression/tie-manual-vertical-tune.ly @@ -0,0 +1,23 @@ +\header { + + + texidoc = "If using integers, the tie will vertically tuned for +staff line avoidance. If using a floating point number, this is taken +as the exact location." + +} + +\version "2.11.11" + +\layout { + ragged-right = ##t +} + +\version "2.10.0" +\relative c'' { + \override Tie #'staff-position = #3 + d4 ~ + \override Tie #'staff-position = #3.0 + d ~ + d + } diff --git a/input/regression/tie-single-chord.ly b/input/regression/tie-single-chord.ly new file mode 100644 index 0000000000..c6160ab8d8 --- /dev/null +++ b/input/regression/tie-single-chord.ly @@ -0,0 +1,17 @@ +\header { + + texidoc = "Tie directions are also scored. In hairy configurations, + the default rule for tie directions is overruled." + +} + +\version "2.11.11" + +\paper +{ + ragged-right = ##t +} + +\relative c' { + <f d'>~ <e f c'> +} diff --git a/lily/include/misc.hh b/lily/include/misc.hh index abef7b96c5..d0bb6f417b 100644 --- a/lily/include/misc.hh +++ b/lily/include/misc.hh @@ -38,6 +38,7 @@ linear_interpolate (Real x, Real x1, Real x2, Real y1, Real y2) Real directed_round (Real f, Direction d); Real peak_around (Real epsilon, Real threshold, Real x); +Real convex_amplifier (Real standard_x, Real x); string camel_case_to_lisp_identifier (string in); #endif diff --git a/lily/include/tie-configuration.hh b/lily/include/tie-configuration.hh index 8801a7be7f..d65604c1dc 100644 --- a/lily/include/tie-configuration.hh +++ b/lily/include/tie-configuration.hh @@ -68,6 +68,7 @@ public: void reset_score (); string card () const; string tie_card (int i) const { return tie_score_cards_[i]; } + string complete_tie_card (vsize i) const; string complete_score_card () const; }; diff --git a/lily/include/tie-details.hh b/lily/include/tie-details.hh index e30f3ed866..18b9127d1e 100644 --- a/lily/include/tie-details.hh +++ b/lily/include/tie-details.hh @@ -21,8 +21,10 @@ struct Tie_details Real stem_gap_; Real between_length_limit_; Real wrong_direction_offset_penalty_; + Real same_dir_as_stem_penalty_; Real min_length_penalty_factor_; Real min_length_; + Real skyline_padding_; Real tip_staff_line_clearance_; Real center_staff_line_clearance_; Real staff_line_collision_penalty_; diff --git a/lily/include/tie-formatting-problem.hh b/lily/include/tie-formatting-problem.hh index c5a71b0314..863712e99e 100644 --- a/lily/include/tie-formatting-problem.hh +++ b/lily/include/tie-formatting-problem.hh @@ -14,30 +14,13 @@ #include "skyline.hh" #include "tie-configuration.hh" #include "tie-details.hh" +#include "tie-specification.hh" #include "tuple.hh" #include <map> #include <set> typedef map< Tuple<int,4>, Tie_configuration *> Tie_configuration_map; - -struct Tie_specification -{ - int position_; - Drul_array<Grob*> note_head_drul_; - Drul_array<int> column_ranks_; - - bool has_manual_position_; - bool has_manual_dir_; - - Real manual_position_; - Direction manual_dir_; - - Tie_specification (); - int column_span () const; - void get_tie_manual_settings (Grob *); -}; - struct Tie_configuration_variation { int index_; @@ -62,11 +45,13 @@ class Tie_formatting_problem Grob *x_refpoint_; - Tie_configuration *get_configuration (int position, Direction dir, Drul_array<int> cols) const; - Tie_configuration *generate_configuration (int position, Direction dir, Drul_array<int> cols) const; + Tie_configuration *get_configuration (int position, Direction dir, Drul_array<int> cols, bool tune_y) const; + Tie_configuration *generate_configuration (int position, Direction dir, Drul_array<int> cols, bool tune_y) const; + vector<Tie_configuration_variation> generate_collision_variations (Ties_configuration const &ties) const; vector<Tie_configuration_variation> generate_extremal_tie_variations (Ties_configuration const &ties) const; - + vector<Tie_configuration_variation> generate_single_tie_variations (Ties_configuration const &ties) const; + void score_configuration (Tie_configuration *) const; Real score_aptitude (Tie_configuration *, Tie_specification const &, Ties_configuration *, int) const; @@ -77,7 +62,7 @@ class Tie_formatting_problem Ties_configuration generate_base_chord_configuration (); Ties_configuration find_best_variation (Ties_configuration const &base, - vector<Tie_configuration_variation> vars); + vector<Tie_configuration_variation> const &vars); public: Tie_details details_; @@ -91,9 +76,8 @@ public: ~Tie_formatting_problem (); Tie_specification get_tie_specification (int) const; - Ties_configuration generate_optimal_chord_configuration (); + Ties_configuration generate_optimal_configuration (); Ties_configuration generate_ties_configuration (Ties_configuration const &); - Tie_configuration find_optimal_tie_configuration (Tie_specification const &) const; void from_ties (vector<Grob*> const &ties); void from_tie (Grob *tie); @@ -103,6 +87,7 @@ public: void set_manual_tie_configuration (SCM); Interval get_attachment (Real, Drul_array<int>) const; Grob *common_x_refpoint () const; + void set_debug_scoring (Ties_configuration const &); }; #endif /* TIE_FORMATTING_PROBLEM_HH */ diff --git a/lily/include/tie-specification.hh b/lily/include/tie-specification.hh new file mode 100644 index 0000000000..912dc78689 --- /dev/null +++ b/lily/include/tie-specification.hh @@ -0,0 +1,36 @@ +/* + tie-specification.hh -- declare Tie_specification + + source file of the GNU LilyPond music typesetter + + (c) 2007 Han-Wen Nienhuys <hanwen@lilypond.org> + +*/ + +#ifndef TIE_SPECIFICATION_HH +#define TIE_SPECIFICATION_HH + +#include "lily-proto.hh" +#include "drul-array.hh" + +struct Tie_specification +{ + int position_; + Drul_array<Grob*> note_head_drul_; + Drul_array<int> column_ranks_; + Grob *tie_grob_; + + bool has_manual_position_; + bool has_manual_dir_; + bool has_manual_delta_y_; + + Real manual_position_; + Direction manual_dir_; + + Tie_specification (); + int column_span () const; + void from_grob (Grob *); +}; + +#endif /* TIE_SPECIFICATION_HH */ + diff --git a/lily/laissez-vibrer-engraver.cc b/lily/laissez-vibrer-engraver.cc index 7a9a4dee72..a6a4bd0e2b 100644 --- a/lily/laissez-vibrer-engraver.cc +++ b/lily/laissez-vibrer-engraver.cc @@ -60,12 +60,20 @@ Laissez_vibrer_engraver::acknowledge_note_head (Grob_info inf) { lv_column_ = make_item ("LaissezVibrerTieColumn", event_->self_scm ()); } - - Grob *lv_tie = make_item ("LaissezVibrerTie", event_->self_scm ()); + + SCM cause = event_->self_scm (); + Grob *lv_tie = make_item ("LaissezVibrerTie", cause); lv_tie->set_object ("note-head", inf.grob ()->self_scm ()); Pointer_group_interface::add_grob (lv_column_, ly_symbol2scm ("ties"), lv_tie); + + if (is_direction (unsmob_stream_event (cause)->get_property ("direction"))) + { + Direction d = to_dir (unsmob_stream_event (cause)->get_property ("direction")); + lv_tie->set_property ("direction", scm_from_int (d)); + } + lv_tie->set_parent (lv_column_, Y_AXIS); lv_ties_.push_back (lv_tie); diff --git a/lily/misc.cc b/lily/misc.cc index c64983d864..8d10141524 100644 --- a/lily/misc.cc +++ b/lily/misc.cc @@ -56,6 +56,14 @@ peak_around (Real epsilon, Real threshold, Real x) return max (- epsilon * (x - threshold) / ((x + epsilon) * threshold), 0.0); } +/* + 0 at 0, 1 at standard_x, and increasing thereafter. + */ +Real +convex_amplifier (Real standard_x, Real x) +{ + return (exp (x / standard_x) - 1.0) / (exp (1) - 1.0); +} string camel_case_to_lisp_identifier (string in) diff --git a/lily/repeat-tie-engraver.cc b/lily/repeat-tie-engraver.cc index bb23f73973..6b16fbc088 100644 --- a/lily/repeat-tie-engraver.cc +++ b/lily/repeat-tie-engraver.cc @@ -60,14 +60,23 @@ Repeat_tie_engraver::acknowledge_note_head (Grob_info inf) { semi_tie_column_ = make_item ("RepeatTieColumn", event_->self_scm ()); } - - Grob *semi_tie = make_item ("RepeatTie", event_->self_scm ()); + + SCM cause = event_->self_scm (); + Grob *semi_tie = make_item ("RepeatTie", cause); semi_tie->set_object ("note-head", inf.grob ()->self_scm ()); Pointer_group_interface::add_grob (semi_tie_column_, ly_symbol2scm ("ties"), semi_tie); semi_tie->set_parent (semi_tie_column_, Y_AXIS); semi_ties_.push_back (semi_tie); + + + if (is_direction (unsmob_stream_event (cause)->get_property ("direction"))) + { + Direction d = to_dir (unsmob_stream_event (cause)->get_property ("direction")); + semi_tie->set_property ("direction", scm_from_int (d)); + } + } ADD_ACKNOWLEDGER (Repeat_tie_engraver, note_head); diff --git a/lily/semi-tie-column.cc b/lily/semi-tie-column.cc index 3713bfdaab..c7d44a9568 100644 --- a/lily/semi-tie-column.cc +++ b/lily/semi-tie-column.cc @@ -55,7 +55,7 @@ Semi_tie_column::calc_positioning_done (SCM smob) SCM manual_configs = me->get_property ("tie-configuration"); problem.set_manual_tie_configuration (manual_configs); - Ties_configuration base = problem.generate_optimal_chord_configuration (); + Ties_configuration base = problem.generate_optimal_configuration (); for (vsize i = 0; i < lv_ties.size(); i++) { SCM cp = Tie::get_control_points (lv_ties[i], problem.common_x_refpoint (), base[i], @@ -63,8 +63,10 @@ Semi_tie_column::calc_positioning_done (SCM smob) lv_ties[i]->set_property ("control-points", cp); set_grob_direction (lv_ties[i], base[i].dir_); - } + problem.set_debug_scoring (base); + } + return SCM_BOOL_T; } diff --git a/lily/tie-column.cc b/lily/tie-column.cc index 1f6738ef6d..9eefbbf8ee 100644 --- a/lily/tie-column.cc +++ b/lily/tie-column.cc @@ -79,14 +79,6 @@ Tie_column::calc_positioning_done (SCM smob) if (!ties.size ()) return SCM_BOOL_T; - if (ties.size() == 1) - { - /* - Already handled by standard mechanisms. - */ - return SCM_BOOL_T; - } - vector_sort (ties, Tie::less); Tie_formatting_problem problem; @@ -96,7 +88,7 @@ Tie_column::calc_positioning_done (SCM smob) problem.set_manual_tie_configuration (manual_configs); - Ties_configuration base = problem.generate_optimal_chord_configuration (); + Ties_configuration base = problem.generate_optimal_configuration (); for (vsize i = 0; i < base.size(); i++) { @@ -108,24 +100,7 @@ Tie_column::calc_positioning_done (SCM smob) set_grob_direction (ties[i], base[i].dir_); -#if DEBUG_TIE_SCORING - if (to_boolean (me->layout () - ->lookup_variable (ly_symbol2scm ("debug-tie-scoring")))) - { - string card = to_string ("%d (%.2f): ", base[i].position_, base[i].delta_y_) - + base[i].card () + base.tie_card (i); - - - if (i == 0) - card += base.card (); - if (i == base.size () - 1) - card += to_string ("TOTAL=%.2f", base.score ()); - - ties[i]->set_property ("quant-score", - ly_string2scm (card)); - } -#endif - + problem.set_debug_scoring (base); } return SCM_BOOL_T; } diff --git a/lily/tie-configuration.cc b/lily/tie-configuration.cc index ea69fe5a78..2491f7e3ef 100644 --- a/lily/tie-configuration.cc +++ b/lily/tie-configuration.cc @@ -158,15 +158,37 @@ Ties_configuration::score () const } +string +Ties_configuration::complete_tie_card (vsize i) const +{ + string s; + s += to_string ("%d (%.2f) %c: ", (*this)[i].position_, (*this)[i].delta_y_, + ((*this)[i].dir_ == UP ? 'u' : 'd')) + + (*this)[i].card () + (*this).tie_card (i); + + /* + this is a little awkward, but we must decide where to put + aggregrates. + */ + if (i == 0) + s += card (); + + if (i + 1 == size ()) + s += to_string ("TOTAL=%.2f", score ()); + + return s; +} + /* for use inside GDB */ string Ties_configuration::complete_score_card () const { - string s = score_card_; - for (vsize i = 0; i < tie_score_cards_.size(); i++) + string s; + for (vsize i = 0; i < size(); i++) { - s += "\n" + tie_score_cards_[i]; + s += complete_tie_card (i); } + return s; } diff --git a/lily/tie-details.cc b/lily/tie-details.cc index c4ed37a2e4..ddb662fd30 100644 --- a/lily/tie-details.cc +++ b/lily/tie-details.cc @@ -54,6 +54,7 @@ Tie_details::from_grob (Grob *me) tie_tie_collision_penalty_ = get_real_detail ("tie-tie-collision-penalty", 30); tie_tie_collision_distance_ = get_real_detail ("tie-tie-collision-distance", .25); horizontal_distance_penalty_factor_ = get_real_detail ("horizontal-distance-penalty-factor", 5); + same_dir_as_stem_penalty_ = get_real_detail ("same-dir-as-stem-penalty", 20); vertical_distance_penalty_factor_ = get_real_detail ("vertical-distance-penalty-factor", 5); intra_space_threshold_ = get_real_detail ("intra-space-threshold", 1.0); outer_tie_length_symmetry_penalty_factor_ = get_real_detail ("outer-tie-length-symmetry-penalty-factor", 3.0); @@ -62,6 +63,7 @@ Tie_details::from_grob (Grob *me) outer_tie_vertical_gap_ = get_real_detail ("outer-tie-vertical-gap", 0.15); single_tie_region_size_ = get_int_detail ("single-tie-region-size", 3); + skyline_padding_ = get_real_detail ("skyline-padding", 0.05); multi_tie_region_size_ = get_int_detail ("multi-tie-region-size", 1); } diff --git a/lily/tie-formatting-problem.cc b/lily/tie-formatting-problem.cc index abcc342181..297fd4a155 100644 --- a/lily/tie-formatting-problem.cc +++ b/lily/tie-formatting-problem.cc @@ -22,7 +22,8 @@ #include "tie-configuration.hh" #include "tie.hh" #include "warn.hh" - +#include "pointer-group-interface.hh" +#include "output-def.hh" void Tie_formatting_problem::print_ties_configuration (Ties_configuration const *ties) @@ -103,6 +104,9 @@ Tie_formatting_problem::set_column_chord_outline (vector<Item*> bounds, Interval x = dots->extent (x_refpoint_, X_AXIS); int p = int (Staff_symbol_referencer::get_position (dots)); + /* + TODO: shouldn't this use column-rank dependent key? + */ dot_positions_.insert (p); dot_x_.unite (x); @@ -115,45 +119,75 @@ Tie_formatting_problem::set_column_chord_outline (vector<Item*> bounds, Tuple2<int> key (column_rank, int (dir)); - if (stem - && !Stem::is_invisible (stem)) + if (stem) { - Interval x; - x.add_point (stem->relative_coordinate (x_refpoint_, X_AXIS)); - x.widen (staff_space / 20); // ugh. - Interval y; - y.add_point (Stem::stem_end_position (stem) * staff_space * .5); - - Direction stemdir = get_grob_direction (stem); - y.add_point (Stem::head_positions (stem)[-stemdir] - * staff_space * .5); - - boxes.push_back (Box (x, y)); + if (Stem::is_normal_stem (stem)) + { + + Interval x; + x.add_point (stem->relative_coordinate (x_refpoint_, X_AXIS)); + x.widen (staff_space / 20); // ugh. + Interval y; + y.add_point (Stem::stem_end_position (stem) * staff_space * .5); + + Direction stemdir = get_grob_direction (stem); + y.add_point (Stem::head_positions (stem)[-stemdir] + * staff_space * .5); + + /* + add extents of stem. + */ + boxes.push_back (Box (x, y)); - stem_extents_[key].unite (Box (x,y)); + stem_extents_[key].unite (Box (x,y)); - if (dir == LEFT) + if (dir == LEFT) + { + Box flag_box = Stem::get_translated_flag (stem).extent_box (); + flag_box.translate( Offset (x[RIGHT], X_AXIS)); + boxes.push_back (flag_box); + } + } + else { - Box flag_box = Stem::get_translated_flag (stem).extent_box (); - flag_box.translate( Offset (x[RIGHT], X_AXIS)); - boxes.push_back (flag_box); + Grob *head = Stem::support_head (stem); + + /* + In case of invisible stem, don't pass x-center of heads. + */ + Real x_center = head->extent (x_refpoint_, X_AXIS).center (); + Interval x_ext; + x_ext[-dir] = x_center; + Interval y_ext; + for (vsize j = 0; j < head_boxes.size (); j++) + y_ext.unite (head_boxes[j][Y_AXIS]); + + boxes.push_back (Box (x_ext, y_ext)); } - } - else if (stem) - { - Grob *head = Stem::support_head (stem); - /* - In case of invisible stem, don't pass x-center of heads. - */ - Real x_center = head->extent (x_refpoint_, X_AXIS).center (); - Interval x_ext; - x_ext[-dir] = x_center; - Interval y_ext; - for (vsize j = 0; j < head_boxes.size (); j++) - y_ext.unite (head_boxes[j][Y_AXIS]); + extract_grob_set (stem, "note-heads", heads); + for (vsize i = 0; i < heads.size(); i ++) + { + if (find (bounds.begin(), bounds.end (), dynamic_cast<Item*> (heads[i])) != bounds.end ()) + continue; - boxes.push_back (Box (x_ext, y_ext)); + /* + other untied notes in the same chord. + */ + + Interval y = Staff_symbol_referencer::extent_in_staff (heads[i]); + Interval x = heads[i]->extent (x_refpoint_, X_AXIS); + boxes.push_back (Box (x, y)); + + + Grob *acc = unsmob_grob (heads[i]->get_object ("accidental-grob")); + if (acc && dir == RIGHT) + { + boxes.push_back (Box (acc->extent (x_refpoint_, X_AXIS), + Staff_symbol_referencer::extent_in_staff (acc))); + } + } + } Direction updowndir = DOWN; @@ -176,7 +210,7 @@ Tie_formatting_problem::set_column_chord_outline (vector<Item*> bounds, while (flip (&updowndir) != DOWN); /* todo: the horizon_padding is somewhat arbitrary */ - chord_outlines_[key] = Skyline (boxes, 0.1, Y_AXIS, -dir); + chord_outlines_[key] = Skyline (boxes, details_.skyline_padding_, Y_AXIS, -dir); if (bounds[0]->break_status_dir ()) { Real x = robust_relative_extent (bounds[0], x_refpoint_, X_AXIS)[-dir]; @@ -273,9 +307,13 @@ Tie_formatting_problem::from_ties (vector<Grob*> const &ties) = dynamic_cast<Item*> (unsmob_grob (ties[i]->get_object ("separation-item"))); if (sep && sep->get_column () == it->get_column ()) it = sep; + + bounds.push_back (it); + } + else + { + bounds.push_back (it); } - - bounds.push_back (it); } set_chord_outline (bounds, d); @@ -286,10 +324,7 @@ Tie_formatting_problem::from_ties (vector<Grob*> const &ties) for (vsize i = 0; i < ties.size (); i++) { Tie_specification spec; - - spec.get_tie_manual_settings (ties[i]); - - + spec.from_grob (ties[i]); do { @@ -324,7 +359,7 @@ Tie_formatting_problem::from_semi_ties (vector<Grob*> const &semi_ties, Directio spec.position_ = int (Staff_symbol_referencer::get_position (head)); } - spec.get_tie_manual_settings (semi_ties[i]); + spec.from_grob (semi_ties[i]); spec.note_head_drul_[head_dir] = head; column_rank = Tie::get_column_rank (semi_ties[i], head_dir); @@ -361,7 +396,8 @@ Tie_formatting_problem::get_tie_specification (int i) const Return configuration, create it if necessary. */ Tie_configuration* -Tie_formatting_problem::get_configuration (int pos, Direction dir, Drul_array<int> columns) const +Tie_formatting_problem::get_configuration (int pos, Direction dir, Drul_array<int> columns, + bool tune_dy) const { int key_components[] = { pos, dir, columns[LEFT], columns[RIGHT] @@ -375,14 +411,14 @@ Tie_formatting_problem::get_configuration (int pos, Direction dir, Drul_array<in } - Tie_configuration *conf = generate_configuration (pos, dir, columns); + Tie_configuration *conf = generate_configuration (pos, dir, columns, tune_dy); ((Tie_formatting_problem*) this)->possibilities_[key] = conf; return conf; } Tie_configuration* Tie_formatting_problem::generate_configuration (int pos, Direction dir, - Drul_array<int> columns) const + Drul_array<int> columns, bool y_tune) const { Tie_configuration *conf = new Tie_configuration; conf->position_ = pos; @@ -392,7 +428,6 @@ Tie_formatting_problem::generate_configuration (int pos, Direction dir, Real y = conf->position_ * 0.5 * details_.staff_space_; - bool y_tune = true; if (dot_positions_.find (pos) != dot_positions_.end ()) { conf->delta_y_ += dir * 0.25 * details_.staff_space_; @@ -539,7 +574,7 @@ Tie_formatting_problem::score_aptitude (Tie_configuration *conf, } { - Real p = details_.vertical_distance_penalty_factor_ * fabs (curve_y - tie_y); + Real p = details_.vertical_distance_penalty_factor_ * convex_amplifier (1.0, fabs (curve_y - tie_y)); if (ties_conf) ties_conf->add_tie_score (p, tie_idx, "vdist"); else @@ -554,22 +589,67 @@ Tie_formatting_problem::score_aptitude (Tie_configuration *conf, Interval head_x = spec.note_head_drul_[d]->extent (x_refpoint_, X_AXIS); Real dist = head_x.distance (conf->attachment_x_[d]); + /* TODO: flatten with log or sqrt. */ - Real p = details_.horizontal_distance_penalty_factor_ * dist; + Real p = details_.horizontal_distance_penalty_factor_ + * convex_amplifier (1.25, dist); if (ties_conf) ties_conf->add_tie_score (p, tie_idx, (d == LEFT) ? "lhdist" : "rhdist"); else penalty += p; + } while (flip (&d) != LEFT); + if (ties_conf + && ties_conf->size() == 1) + { + Direction d = LEFT; + Drul_array<Grob*> stems (0, 0); + do + { + if (!spec.note_head_drul_[d]) + continue; + + Grob *stem = unsmob_grob (spec.note_head_drul_[d]->get_object ("stem")); + if (stem + && Stem::is_normal_stem (stem)) + stems[d] = stem; + } + while (flip (&d) != LEFT); + + bool tie_stem_dir_ok = true; + bool tie_position_dir_ok = true; + if (stems[LEFT] && !stems[RIGHT]) + tie_stem_dir_ok = conf->dir_ != get_grob_direction (stems[LEFT]); + else if (!stems[LEFT] && stems[RIGHT]) + tie_stem_dir_ok = conf->dir_ != get_grob_direction (stems[RIGHT]); + else if (stems[LEFT] && stems[RIGHT] + && get_grob_direction (stems[LEFT]) == get_grob_direction (stems[RIGHT])) + tie_stem_dir_ok = conf->dir_ != get_grob_direction (stems[LEFT]); + else if (spec.position_) + tie_position_dir_ok = conf->dir_ == sign (spec.position_); + + if (!tie_stem_dir_ok) + ties_conf->add_score (details_.same_dir_as_stem_penalty_, "tie/stem dir"); + if (!tie_position_dir_ok) + ties_conf->add_score (details_.same_dir_as_stem_penalty_, "tie/pos dir"); + } + while (flip (&d) != LEFT); + + return penalty; } + +/* + Score a configuration, ie. how well these ties looks without regard + to the note heads that they should connect to. + */ void Tie_formatting_problem::score_configuration (Tie_configuration *conf) const { @@ -598,7 +678,7 @@ Tie_formatting_problem::score_configuration (Tie_configuration *conf) const conf->add_score ( details_.staff_line_collision_penalty_ * peak_around (0.1 * details_.center_staff_line_clearance_, - details_.center_staff_line_clearance_, + details_.center_staff_line_clearance_, fabs (top_pos - round_top_pos)), "line center"); } @@ -639,96 +719,6 @@ Tie_formatting_problem::score_configuration (Tie_configuration *conf) const conf->scored_ = true; } -Tie_configuration -Tie_formatting_problem::find_optimal_tie_configuration (Tie_specification const &spec) const -{ - vector<Tie_configuration*> confs; - - int pos = spec.position_; - Direction dir = spec.manual_dir_; - - for (int i = 0; i < details_.single_tie_region_size_; i ++) - { - confs.push_back (generate_configuration (pos + i * dir, dir, - spec.column_ranks_)); - - if (spec.has_manual_position_) - { - confs.back ()->delta_y_ - = (spec.manual_position_ - spec.position_) - * 0.5 * details_.staff_space_; - - break; - } - } - - vector<Real> scores; - - int best_idx = -1; - Real best_score = 1e6; - for (vsize i = 0; i < confs.size (); i ++) - { - score_configuration (confs[i]); - Real score = score_aptitude (confs[i], spec, 0, 0) - + confs[i]->score (); - - if (score < best_score) - { - best_score = score; - best_idx = i; - } - } - - if (best_idx < 0) - programming_error ("No best tie configuration found."); - - Tie_configuration best - = (best_idx >= 0) ? *confs[best_idx] : *confs[0]; - - for (vsize i = 0; i < confs.size (); i++) - delete confs[i]; - - return best; -} - -Tie_specification::Tie_specification () -{ - has_manual_position_ = false; - has_manual_dir_ = false; - position_ = 0; - manual_position_ = 0; - manual_dir_ = CENTER; - note_head_drul_[LEFT] = - note_head_drul_[RIGHT] = 0; - column_ranks_[RIGHT] = - column_ranks_[LEFT] = 0; -} - - -void -Tie_specification::get_tie_manual_settings (Grob *tie) -{ - if (scm_is_number (tie->get_property_data ("direction"))) - { - manual_dir_ = to_dir (tie->get_property ("direction")); - has_manual_dir_ = true; - } - - position_ = Tie::get_position (tie); - if (scm_is_number (tie->get_property ("staff-position"))) - { - manual_position_ = scm_to_double (tie->get_property ("staff-position")); - has_manual_position_ = true; - position_ = int (my_round (manual_position_)); - } -} - -int -Tie_specification::column_span () const -{ - return column_ranks_[RIGHT] - column_ranks_[LEFT]; -} - void Tie_formatting_problem::score_ties_aptitude (Ties_configuration *ties) const { @@ -794,21 +784,23 @@ Tie_formatting_problem::score_ties_configuration (Ties_configuration *ties) cons last_center = center; } - ties->add_score (details_.outer_tie_length_symmetry_penalty_factor_ - * fabs (ties->at (0).attachment_x_.length () - ties->back ().attachment_x_.length ()), - "length symm"); + if (ties->size () > 1) + { + ties->add_score (details_.outer_tie_length_symmetry_penalty_factor_ + * fabs (ties->at (0).attachment_x_.length () - ties->back ().attachment_x_.length ()), + "length symm"); - ties->add_score (details_.outer_tie_vertical_distance_symmetry_penalty_factor_ - * fabs (fabs (specifications_[0].position_ * 0.5 * details_.staff_space_ - - (ties->at (0).position_ * 0.5 * details_.staff_space_ - + ties->at (0).delta_y_)) - - - fabs (specifications_.back ().position_ * 0.5 * details_.staff_space_ - - (ties->back ().position_ * 0.5 * details_.staff_space_ - + ties->back ().delta_y_))), - "pos symmetry"); + ties->add_score (details_.outer_tie_vertical_distance_symmetry_penalty_factor_ + * fabs (fabs (specifications_[0].position_ * 0.5 * details_.staff_space_ + - (ties->at (0).position_ * 0.5 * details_.staff_space_ + + ties->at (0).delta_y_)) + - + fabs (specifications_.back ().position_ * 0.5 * details_.staff_space_ + - (ties->back ().position_ * 0.5 * details_.staff_space_ + + ties->back ().delta_y_))), + "pos symmetry"); + } } - /* Generate with correct X-attachments and beziers, copying delta_y_ from TIES_CONFIG if necessary. @@ -820,8 +812,9 @@ Tie_formatting_problem::generate_ties_configuration (Ties_configuration const &t for (vsize i = 0; i < ties_config.size (); i++) { Tie_configuration * ptr = get_configuration (ties_config[i].position_, ties_config[i].dir_, - ties_config[i].column_ranks_); - if (specifications_[i].has_manual_position_) + ties_config[i].column_ranks_, + !specifications_[i].has_manual_delta_y_); + if (specifications_[i].has_manual_delta_y_) { ptr->delta_y_ = (specifications_[i].manual_position_ - ties_config[i].position_) @@ -845,8 +838,9 @@ Tie_formatting_problem::generate_base_chord_configuration () if (specifications_[i].has_manual_position_) { conf.position_ = (int) my_round (specifications_[i].manual_position_); - conf.delta_y_ = (specifications_[i].manual_position_ - conf.position_) - * 0.5 * details_.staff_space_; + if (specifications_[i].has_manual_delta_y_) + conf.delta_y_ = (specifications_[i].manual_position_ - conf.position_) + * 0.5 * details_.staff_space_; } else { @@ -869,7 +863,7 @@ Tie_formatting_problem::generate_base_chord_configuration () Ties_configuration Tie_formatting_problem::find_best_variation (Ties_configuration const &base, - vector<Tie_configuration_variation> vars) + vector<Tie_configuration_variation> const &vars) { Ties_configuration best = base; @@ -897,16 +891,24 @@ Tie_formatting_problem::find_best_variation (Ties_configuration const &base, Ties_configuration -Tie_formatting_problem::generate_optimal_chord_configuration () +Tie_formatting_problem::generate_optimal_configuration () { Ties_configuration base = generate_base_chord_configuration (); - vector<Tie_configuration_variation> vars = generate_collision_variations (base); - score_ties (&base); + + vector<Tie_configuration_variation> vars; + if (specifications_.size () > 1) + vars = generate_collision_variations (base); + else + vars = generate_single_tie_variations (base); + Ties_configuration best = find_best_variation (base, vars); - vars = generate_extremal_tie_variations (best); - best = find_best_variation (best, vars); + if (specifications_.size () > 1) + { + vars = generate_extremal_tie_variations (best); + best = find_best_variation (best, vars); + } return best; } @@ -990,7 +992,8 @@ Tie_formatting_problem::generate_extremal_tie_variations (Ties_configuration con var.index_ = (d == DOWN) ? 0 : ties.size () - 1; var.suggestion_ = get_configuration (boundary (ties, d, 0).position_ + d * i, d, - boundary (ties, d, 0).column_ranks_); + boundary (ties, d, 0).column_ranks_, + true); vars.push_back (var); } } @@ -999,6 +1002,41 @@ Tie_formatting_problem::generate_extremal_tie_variations (Ties_configuration con return vars; } +vector<Tie_configuration_variation> +Tie_formatting_problem::generate_single_tie_variations (Ties_configuration const &ties) const +{ + vector<Tie_configuration_variation> vars; + + int sz = details_.single_tie_region_size_; + if (specifications_[0].has_manual_position_) + sz = 1; + for (int i = 0; i < sz; i ++) + { + Direction d = LEFT; + do + { + if (i == 0 + && ties[0].dir_ == d) + continue; + + int p = ties[0].position_ + i * d; + + if (!specifications_[0].has_manual_dir_ + || d == specifications_[0].manual_dir_) + { + Tie_configuration_variation var; + var.index_ = 0; + var.suggestion_ = get_configuration (p, + d, specifications_[0].column_ranks_, + !specifications_[0].has_manual_delta_y_); + vars.push_back (var); + } + } + while (flip (&d) != LEFT); + } + return vars; +} + vector<Tie_configuration_variation> Tie_formatting_problem::generate_collision_variations (Ties_configuration const &ties) const @@ -1025,7 +1063,8 @@ Tie_formatting_problem::generate_collision_variations (Ties_configuration const - ties[i].dir_, - ties[i].dir_, - ties[i].column_ranks_ + ties[i].column_ranks_, + !specifications_[i].has_manual_delta_y_ ); vars.push_back (var); @@ -1038,7 +1077,9 @@ Tie_formatting_problem::generate_collision_variations (Ties_configuration const var.suggestion_ = get_configuration (specifications_[i-1].position_ - ties[i-1].dir_, - ties[i-1].dir_, - specifications_[i-1].column_ranks_); + specifications_[i-1].column_ranks_, + !specifications_[i-1].has_manual_delta_y_ + ); vars.push_back (var); } @@ -1049,7 +1090,10 @@ Tie_formatting_problem::generate_collision_variations (Ties_configuration const Tie_configuration_variation var; var.index_ = i-1; var.suggestion_ = get_configuration (specifications_[i-1].position_ - 1, DOWN, - specifications_[i-1].column_ranks_); + specifications_[i-1].column_ranks_, + !specifications_[i-1].has_manual_delta_y_ + + ); vars.push_back (var); } if (i == ties.size() && !specifications_[i].has_manual_position_ @@ -1059,7 +1103,9 @@ Tie_formatting_problem::generate_collision_variations (Ties_configuration const var.index_ = i; var.suggestion_ = get_configuration (specifications_[i].position_ + 1, UP, - specifications_[i].column_ranks_); + specifications_[i].column_ranks_, + !specifications_[i].has_manual_delta_y_ + ); vars.push_back (var); } } @@ -1070,7 +1116,9 @@ Tie_formatting_problem::generate_collision_variations (Ties_configuration const var.index_ = i; var.suggestion_ = get_configuration (ties[i].position_ + ties[i].dir_, ties[i].dir_, - ties[i].column_ranks_); + ties[i].column_ranks_, + !specifications_[i].has_manual_delta_y_ + ); vars.push_back (var); } @@ -1099,7 +1147,9 @@ Tie_formatting_problem::set_manual_tie_configuration (SCM manual_configs) { spec.has_manual_position_ = true; spec.manual_position_ = scm_to_double (scm_car (entry)); + spec.has_manual_delta_y_ = (scm_inexact_p (scm_car (entry)) == SCM_BOOL_T); } + if (scm_is_number (scm_cdr (entry))) { spec.has_manual_dir_ = true; @@ -1110,3 +1160,20 @@ Tie_formatting_problem::set_manual_tie_configuration (SCM manual_configs) } } + +void +Tie_formatting_problem::set_debug_scoring (Ties_configuration const &base) +{ +#if DEBUG_TIE_SCORING + if (to_boolean (x_refpoint_->layout () + ->lookup_variable (ly_symbol2scm ("debug-tie-scoring")))) + { + for (vsize i = 0; i < base.size(); i++) + { + string card = base.complete_tie_card (i); + specifications_[i].tie_grob_->set_property ("quant-score", + ly_string2scm (card)); + } + } +#endif +} diff --git a/lily/tie-specification.cc b/lily/tie-specification.cc new file mode 100644 index 0000000000..3a7ce345cb --- /dev/null +++ b/lily/tie-specification.cc @@ -0,0 +1,47 @@ +#include "tie-formatting-problem.hh" +#include "grob.hh" +#include "tie.hh" +#include "libc-extension.hh" +#include "tie-specification.hh" + +Tie_specification::Tie_specification () +{ + tie_grob_ = 0; + has_manual_position_ = false; + has_manual_dir_ = false; + has_manual_delta_y_ = false; + position_ = 0; + manual_position_ = 0; + manual_dir_ = CENTER; + note_head_drul_[LEFT] = + note_head_drul_[RIGHT] = 0; + column_ranks_[RIGHT] = + column_ranks_[LEFT] = 0; +} + + +void +Tie_specification::from_grob (Grob *tie) +{ + tie_grob_ = tie; + if (scm_is_number (tie->get_property_data ("direction"))) + { + manual_dir_ = to_dir (tie->get_property ("direction")); + has_manual_dir_ = true; + } + + position_ = Tie::get_position (tie); + SCM pos_scm = tie->get_property ("staff-position"); + if (scm_is_number (pos_scm)) + { + has_manual_delta_y_ = (scm_inexact_p (pos_scm) == SCM_BOOL_T); + manual_position_ = scm_to_double (tie->get_property ("staff-position")); + has_manual_position_ = true; + } +} + +int +Tie_specification::column_span () const +{ + return column_ranks_[RIGHT] - column_ranks_[LEFT]; +} diff --git a/lily/tie.cc b/lily/tie.cc index 0c03c0fcb5..1d4e7fca9a 100644 --- a/lily/tie.cc +++ b/lily/tie.cc @@ -148,7 +148,8 @@ Tie::calc_direction (SCM smob) if ((Tie_column::has_interface (yparent) || Semi_tie_column::has_interface (yparent)) && unsmob_grob_array (yparent->get_object ("ties")) - && unsmob_grob_array (yparent->get_object ("ties"))->size () > 1) + // && unsmob_grob_array (yparent->get_object ("ties"))->size () > 1 + ) { /* trigger positioning. */ (void) yparent->get_property ("positioning-done"); @@ -170,18 +171,17 @@ Tie::get_default_control_points (Grob *me_grob) Tie_formatting_problem problem; problem.from_tie (me); + Tie_specification spec = problem.get_tie_specification (0); - spec.has_manual_dir_ = true; - spec.manual_dir_ = get_grob_direction (me); - if (!me->is_live ()) return SCM_EOL; - Tie_configuration conf - = problem.find_optimal_tie_configuration (spec); + + Ties_configuration conf + = problem.generate_optimal_configuration (); return get_control_points (me, problem.common_x_refpoint (), - conf, problem.details_); + conf[0], problem.details_); } SCM @@ -211,14 +211,12 @@ Tie::calc_control_points (SCM smob) { Grob *me = unsmob_grob (smob); - // trigger Tie-column - (void) get_grob_direction (me); - Grob *yparent = me->get_parent (Y_AXIS); if ((Tie_column::has_interface (yparent) || Semi_tie_column::has_interface (yparent)) && unsmob_grob_array (yparent->get_object ("ties")) - && unsmob_grob_array (yparent->get_object ("ties"))->size () > 1) + // && unsmob_grob_array (yparent->get_object ("ties"))->size () > 1 + ) { /* trigger positioning. */ (void) yparent->get_property ("positioning-done"); @@ -234,7 +232,7 @@ Tie::calc_control_points (SCM smob) } /* - TODO: merge witnh Slur::print. + TODO: merge with Slur::print. */ MAKE_SCHEME_CALLBACK (Tie, print, 1); SCM @@ -283,8 +281,15 @@ Tie::print (SCM smob) Stencil tm = *unsmob_stencil (Text_interface::interpret_markup (me->layout ()->self_scm (), properties, quant_score)); - tm.translate_axis (b.control_[0][Y_AXIS]*2, Y_AXIS); - a.add_at_edge (X_AXIS, RIGHT, tm, 1.0, 0); + tm.translate (Offset (b.control_[3][X_AXIS] + 0.5, + b.control_[0][Y_AXIS] * 2)); + tm = tm.in_color (1, 0, 0); + + /* + It would be nice if we could put this in a different layer, + but alas, this must be done with a Tie override. + */ + a.add_stencil (tm); } #endif diff --git a/scm/define-grobs.scm b/scm/define-grobs.scm index 8b06fb5927..9acbe3beaf 100644 --- a/scm/define-grobs.scm +++ b/scm/define-grobs.scm @@ -1822,14 +1822,17 @@ (stem-gap . 0.35) (height-limit . 1.0) (horizontal-distance-penalty-factor . 10) + (same-dir-as-stem-penalty . 8) (min-length-penalty-factor . 20) (tie-tie-collision-distance . 0.45) (tie-tie-collision-penalty . 25.0) (intra-space-threshold . 1.25) (outer-tie-vertical-distance-symmetry-penalty-factor . 10) (outer-tie-length-symmetry-penalty-factor . 10) + (vertical-distance-penalty-factor . 7) (outer-tie-vertical-gap . 0.25) (multi-tie-region-size . 1) + (single-tie-region-size . 4) (between-length-limit . 1.0))) (thickness . 1.2) |