2005-12-13 Han-Wen Nienhuys <hanwen@xs4all.nl>
+ * 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
@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]
+ <b d f g> ~ <b d f g>
+@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,
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;
/*
- tie-column-format.hh -- declare
+ tie-column-format.hh -- declare Tie column format routines.
source file of the GNU LilyPond music typesetter
#ifndef TIE_COLUMN_FORMAT_HH
#define TIE_COLUMN_FORMAT_HH
-
+#include "lily-proto.hh"
+#include "tie-configuration.hh"
void set_chord_outline (Array<Skyline_entry> *skyline,
Link_array<Item> bounds,
--- /dev/null
+/*
+ tie-configuration.hh -- declare Tie_configuration
+
+ source file of the GNU LilyPond music typesetter
+
+ (c) 2005 Han-Wen Nienhuys <hanwen@xs4all.nl>
+
+*/
+
+#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<Tie_configuration> Ties_configuration;
+
+#endif /* TIE_CONFIGURATION_HH */
+
+
+
#include "parray.hh"
#include "skyline.hh"
#include "lily-proto.hh"
+#include "tie-configuration.hh"
#include <map>
#include <set>
typedef map< pair<int, int>, 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<Skyline_entry> > chord_outlines_;
set<int> dot_positions_;
Interval dot_x_;
+ Array<Tie_specification> 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<Tie_configuration_variation> 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_;
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<Grob> const &ties);
void from_tie (Grob *tie);
void from_lv_ties (Link_array<Grob> const &);
void set_chord_outline (Link_array<Item>, Direction);
-
+ void set_manual_tie_configuration (SCM);
Interval get_attachment (Real) const;
Grob *common_x_refpoint () const;
};
-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<Tie_configuration> ties_;
-};
class Tie
{
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&);
static Interval get_default_attachments (Spanner *me, Grob *common, Real gap,
int *staff_position, bool *in_between,
Tie_details const &);
-
};
#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,
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;
#include "directional-element-interface.hh"
#include "rhythmic-head.hh"
#include "tie-formatting-problem.hh"
+#include "tie-configuration.hh"
#include <set>
-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,
Tie_details const &details)
{
set<int> 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
conf.center_tie_vertically (details);
}
-void
-set_tie_config_directions (Ties_configuration *tie_configs_ptr)
-{
- Array<Tie_configuration> &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;
- }
-}
#include "directional-element-interface.hh"
#include "tie-column-format.hh"
#include "tie-formatting-problem.hh"
+#include "tie-configuration.hh"
using namespace std;
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;
}
--- /dev/null
+/*
+ tie-configuration.cc -- implement Tie_configuration
+
+ source file of the GNU LilyPond music typesetter
+
+ (c) 2005 Han-Wen Nienhuys <hanwen@xs4all.nl>
+
+*/
+
+#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];
+}
#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
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
details_.from_grob (lv_ties[0]);
Link_array<Item> 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);
}
}
Tie_configuration*
-Tie_formatting_problem::get_configuration (int pos, Direction dir)
+Tie_formatting_problem::get_configuration (int pos, Direction dir)
{
pair<int,int> key (pos, dir);
Tie_configuration_map::const_iterator f = possibilities_.find (key);
}
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;
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_;
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;
}
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<Tie_configuration> confs;
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<Tie_configuration_variation> 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_configuration_variation>
+Tie_formatting_problem::get_variations (Ties_configuration const &ties)
+{
+ Real center_distance_tolerance = 0.25;
+
+ Array<Tie_configuration_variation> 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 ++;
+ }
+}
+
#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)
{
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_;
#include "tie-column.hh"
#include "grob-array.hh"
#include "tie-formatting-problem.hh"
+#include "tie-configuration.hh"
int
return attachments;
}
-void
-Tie::get_configuration (Grob *me_grob,
- Tie_configuration *conf,
- Tie_formatting_problem const &problem)
-{
- Spanner *me = dynamic_cast<Spanner*> (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)
{