]> git.donarmstrong.com Git - lilypond.git/blobdiff - lily/tie-formatting-problem.cc
* Documentation/pictures/GNUmakefile (local-dist): loose the rule
[lilypond.git] / lily / tie-formatting-problem.cc
index 0ea0f8c966dfcf37c4258d1adda5ccdc83d5e708..8d40e75f71784c9f4aad5ed497fd7abbd824c656 100644 (file)
@@ -1,26 +1,37 @@
 /*
-  tie-formatting-problem.cc -- implement Tie_formatting_problem6
+  tie-formatting-problem.cc -- implement Tie_formatting_problem
 
   source file of the GNU LilyPond music typesetter
 
-  (c) 2005 Han-Wen Nienhuys <hanwen@xs4all.nl>
+  (c) 2005--2006 Han-Wen Nienhuys <hanwen@xs4all.nl>
 
 */
 
 #include "tie-formatting-problem.hh"
 
-#include "tie-configuration.hh"
+#include "bezier.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 "libc-extension.hh"
 #include "note-head.hh"
 #include "rhythmic-head.hh"
+#include "spanner.hh" 
+#include "staff-symbol-referencer.hh"
+#include "stem.hh"
+#include "tie-configuration.hh"
+#include "tie.hh"
 #include "warn.hh"
 
+/*
+   0 at threshold,  1 at 0, with 1/x falloff.
+ */
+Real peak_around (Real epsilon,  Real threshold, Real x)
+{
+  if (x < 0)
+    return 1.0;
+  return max (- epsilon * (x - threshold) / ((x + epsilon)  * threshold), 0.0);
+}
+
 Interval
 Tie_formatting_problem::get_attachment (Real y) const
 {
@@ -48,15 +59,16 @@ Tie_formatting_problem::~Tie_formatting_problem ()
 }
 
 void
-Tie_formatting_problem::set_chord_outline (Link_array<Item> bounds,
+Tie_formatting_problem::set_chord_outline (vector<Item*> bounds,
                                           Direction d)
 {
   Real staff_space = Staff_symbol_referencer::staff_space (bounds[0]);
 
-  Array<Box> boxes;
+  vector<Box> boxes;
+  vector<Box> head_boxes;
 
   Grob *stem = 0;
-  for (int i = 0; i < bounds.size (); i++)
+  for (vsize i = 0; i < bounds.size (); i++)
     {
       Grob *head = bounds[i];
       if (!Note_head::has_interface (head))
@@ -70,33 +82,33 @@ Tie_formatting_problem::set_chord_outline (Link_array<Item> bounds,
                  (p+1) * 0.5 * staff_space);
 
       Interval x = head->extent (x_refpoint_, X_AXIS);
-      boxes.push (Box (x, y));
+      head_boxes.push_back (Box (x, y));
+      boxes.push_back (Box (x, y));
 
       Grob *dots = Rhythmic_head::get_dots (head);
       if (d == LEFT && dots)
        {
          Interval x = dots->extent (x_refpoint_, X_AXIS);
-         Interval y (-0.5, 0.5);
          int p = int (Staff_symbol_referencer::get_position (dots));
-         y.translate (p);
 
          dot_positions_.insert (p);
          dot_x_.unite (x);
+
+         Interval y (dots->extent (dots, Y_AXIS));
+         y.translate (p * staff_space * 0.5);
          
-         y *= staff_space * 0.5;
-         // boxes.push (Box (x, y));
+         boxes.push_back (Box (x, y));
        }
     }
 
   chord_outlines_[d] = empty_skyline (-d);
-
   if (bounds[0]->break_status_dir ())
     {
       Real x = robust_relative_extent (bounds[0],  x_refpoint_, X_AXIS)[-d];
-      chord_outlines_[d].elem_ref (0).height_ = x; 
+      chord_outlines_[d].at (0).height_ = x; 
     }
          
-  for (int i = 0; i < boxes.size (); i++)
+  for (vsize i = 0; i < boxes.size (); i++)
     insert_extent_into_skyline (&chord_outlines_[d]  ,
                                boxes[i], Y_AXIS, -d);
 
@@ -115,7 +127,7 @@ Tie_formatting_problem::set_chord_outline (Link_array<Item> bounds,
          
       insert_extent_into_skyline (&chord_outlines_[d], Box (x,y), Y_AXIS, -d);
 
-
+      stem_extents_[d].unite (Box (x,y));
 
       if (d == LEFT)
        {
@@ -129,11 +141,11 @@ Tie_formatting_problem::set_chord_outline (Link_array<Item> bounds,
   Direction updowndir = DOWN;
   do
     {
-      Interval x ;
+      Interval x;
       Interval y;
-      if (boxes.size())
+      if (head_boxes.size())
        {
-         Box b = boxes.boundary (updowndir, 0);
+         Box b = boundary (head_boxes, updowndir, 0);
          x = b[X_AXIS];
          x[-d] =  b[X_AXIS].linear_combination (-d / 2);
          y[-updowndir] = b[Y_AXIS][updowndir];
@@ -152,9 +164,8 @@ Tie_formatting_problem::set_chord_outline (Link_array<Item> bounds,
 void
 Tie_formatting_problem::from_tie (Grob *tie)
 {
-  Link_array<Grob> ties;
-  ties.push (tie);
-
+  vector<Grob*> ties;
+  ties.push_back (tie);
   from_ties (ties);
 
   details_.from_grob (tie);
@@ -167,13 +178,13 @@ Tie_formatting_problem::common_x_refpoint () const
 }
 
 void
-Tie_formatting_problem::from_ties (Link_array<Grob> const &ties)
+Tie_formatting_problem::from_ties (vector<Grob*> const &ties)
 {
-  if (ties.is_empty ())
+  if (ties.empty ())
     return;
   
   x_refpoint_ = ties[0];
-  for (int i = 0; i < ties.size (); i++)
+  for (vsize i = 0; i < ties.size (); i++)
     {
       x_refpoint_ = dynamic_cast<Spanner*> (ties[i])->get_bound (LEFT)->common_refpoint (x_refpoint_, X_AXIS); 
       x_refpoint_ = dynamic_cast<Spanner*> (ties[i])->get_bound (RIGHT)->common_refpoint (x_refpoint_, X_AXIS); 
@@ -184,13 +195,13 @@ Tie_formatting_problem::from_ties (Link_array<Grob> const &ties)
   Direction d = LEFT;
   do
     {
-      Link_array<Item> bounds;
+      vector<Item*> bounds;
       
-      for (int i = 0; i < ties.size (); i++)
+      for (vsize i = 0; i < ties.size (); i++)
        {
          Item *it = dynamic_cast<Spanner*> (ties[i])->get_bound (d);
                                             
-         bounds.push (it);
+         bounds.push_back (it);
        }
       
       set_chord_outline (bounds, d);
@@ -198,7 +209,7 @@ Tie_formatting_problem::from_ties (Link_array<Grob> const &ties)
   while (flip (&d) != LEFT);
 
 
-  for (int i = 0; i < ties.size (); i++)
+  for (vsize i = 0; i < ties.size (); i++)
     {
       Tie_specification spec;
       
@@ -209,21 +220,27 @@ Tie_formatting_problem::from_ties (Link_array<Grob> const &ties)
        }
          
       spec.position_ = Tie::get_position (ties[i]);
+
+      do
+       {
+         spec.note_head_drul_[d] = Tie::head (ties[i], d);
+       }
+      while (flip (&d) != LEFT);
       
-      specifications_.push (spec);
+      specifications_.push_back (spec);
     }
 }
 
 void
-Tie_formatting_problem::from_lv_ties (Link_array<Grob> const &lv_ties)
+Tie_formatting_problem::from_lv_ties (vector<Grob*> const &lv_ties)
 {
-  if (lv_ties.is_empty ())
-    return ;
+  if (lv_ties.empty ())
+    return;
   
   details_.from_grob (lv_ties[0]);
-  Link_array<Item> heads;
+  vector<Item*> heads;
   
-  for (int i = 0; i < lv_ties.size (); i++)
+  for (vsize i = 0; i < lv_ties.size (); i++)
     {
       Tie_specification spec;
       Item *head = unsmob_item (lv_ties[i]->get_object ("note-head"));
@@ -235,22 +252,23 @@ Tie_formatting_problem::from_lv_ties (Link_array<Grob> const &lv_ties)
        {
          spec.position_ = int (Staff_symbol_referencer::get_position (head));
        }
-      
-      heads.push (head);
-      specifications_.push (spec);
+
+      spec.note_head_drul_[LEFT] = head;
+      heads.push_back (head);
+      specifications_.push_back (spec);
     }
 
   x_refpoint_ = lv_ties [0];
-  for (int i = 0; i < lv_ties.size (); i++)
-    {
-      x_refpoint_ = lv_ties[i]->common_refpoint (x_refpoint_, X_AXIS); 
-    }
+  for (vsize i = 0; i < lv_ties.size (); i++)
+    x_refpoint_ = lv_ties[i]->common_refpoint (x_refpoint_, X_AXIS); 
+  for (vsize i = 0; i < heads.size (); i++)
+    x_refpoint_ = heads[i]->common_refpoint (x_refpoint_, X_AXIS); 
 
   set_chord_outline (heads, LEFT);
 
   Real right_most = - infinity_f;   
 
-  for (int i = 0; i < chord_outlines_[LEFT].size (); i++)
+  for (vsize i = 0; i < chord_outlines_[LEFT].size (); i++)
     {
       right_most = max (right_most, chord_outlines_[LEFT][i].height_);
     }
@@ -259,9 +277,17 @@ Tie_formatting_problem::from_lv_ties (Link_array<Grob> const &lv_ties)
   right_entry.width_.set_full ();
   right_entry.height_ = right_most + 1.5;
   
-  chord_outlines_[RIGHT].push (right_entry);
+  chord_outlines_[RIGHT].push_back (right_entry);
 }
 
+
+Tie_specification
+Tie_formatting_problem::get_tie_specification (int i) const
+{
+  return specifications_[i];
+}
+
+
 Tie_configuration*
 Tie_formatting_problem::get_configuration (int pos, Direction dir) 
 {
@@ -274,7 +300,7 @@ Tie_formatting_problem::get_configuration (int pos, Direction dir)
     }
 
   
-  Tie_configuration *conf = generate_configuration (pos,dir);
+  Tie_configuration *conf = generate_configuration (pos, dir);
   possibilities_[key] = conf;
   return conf;
 }
@@ -295,60 +321,121 @@ Tie_formatting_problem::generate_configuration (int pos, Direction dir) const
   conf->attachment_x_ = get_attachment (y + conf->delta_y_);
 
   Real h =  conf->height (details_);
+
+  if (h <  details_.intra_space_threshold_ * 0.5 * details_.staff_space_)
+    {
+      /*
+       This is less sensible for long ties, since those are more
+       horizontal.
+      */
+      Interval close_by = get_attachment (y
+                                         + conf->delta_y_
+                                         + (dir * details_.intra_space_threshold_ * 0.25
+                                            * details_.staff_space_));
+      
+      conf->attachment_x_.intersect (close_by);
+    }
+  
   if (!conf->delta_y_)
     {
-      if (h < 0.5 * details_.staff_space_
-         && !Staff_symbol_referencer::on_staffline (details_.staff_symbol_referencer_, pos))
+      /*
+       TODO:
+
+       - should make sliding criterion, should flatten ties if
+
+       - they're just the wrong (ie. touching line at top & bottom)
+       size.
+       
+       */
+      if (h < details_.intra_space_threshold_ * 0.5 * details_.staff_space_)
        {
-         conf->center_tie_vertically (details_);
+         if (!Staff_symbol_referencer::on_line (details_.staff_symbol_referencer_, pos)
+             && abs (pos) < 2 * Staff_symbol_referencer::staff_radius (details_.staff_symbol_referencer_))
+           {
+             conf->center_tie_vertically (details_);
+           }
+         else if (Staff_symbol_referencer::on_line (details_.staff_symbol_referencer_, pos))
+           {
+             conf->delta_y_ += dir *
+               details_.tip_staff_line_clearance_ * 0.5 *  details_.staff_space_;
+           }
        }
-      else if (h < 0.5 * details_.staff_space_
-              && Staff_symbol_referencer::on_staffline (details_.staff_symbol_referencer_, pos))
+      else 
        {
-         conf->delta_y_ += dir * 0.2 * details_.staff_space_;
+         Real top_y = y + conf->delta_y_ + conf->dir_ * h;
+         Real top_pos = top_y / (0.5*details_.staff_space_);
+         int round_pos = int (my_round (top_pos));
+
+         /* TODO: should use other variable? */
+         Real clearance = details_.center_staff_line_clearance_;
+         if (fabs (top_pos - round_pos) < clearance
+             && Staff_symbol_referencer::on_staff_line (details_.staff_symbol_referencer_,
+                                                        round_pos))
+           {
+             Real new_y = (round_pos + clearance * conf->dir_) * 0.5 * details_.staff_space_;
+             conf->delta_y_ = (new_y - top_y);
+           }
        }
     }
   
+  /*
+    we don't recompute attachment_x_ to take changed Y (through
+    delta_Y) into account. Doing would make ties go into small holes between heads, which
+    means we get collisions with neighboring heads.
+   */
   conf->attachment_x_.widen ( - details_.x_gap_);
+
+  Direction d = LEFT;
+  do
+    {
+      Real y = conf->position_ * details_.staff_space_ * 0.5 + conf->delta_y_;
+      if (stem_extents_[d][X_AXIS].is_empty ()
+         || !stem_extents_[d][Y_AXIS].contains (y))
+       continue;
+
+      conf->attachment_x_[d] =
+       d* min (d * conf->attachment_x_[d],
+               d * (stem_extents_[d][X_AXIS][-d] - d * details_.stem_gap_));
+    }
+  while (flip (&d) != LEFT);
   return conf;
 }
 
 Real
 Tie_formatting_problem::score_aptitude (Tie_configuration const &conf,
-                                       int tie_position) const
+                                       Tie_specification const &spec) const
 {
-  Real wrong_direction_offset_penalty_;
-  Real distance_penalty_factor_;
-                                 
-  wrong_direction_offset_penalty_ = 10;
-  distance_penalty_factor_ = 5;
-  
   Real penalty = 0.0;
   Real curve_y = conf.position_ * details_.staff_space_ * 0.5 + conf.delta_y_;
-  Real tie_y = tie_position * details_.staff_space_ * 0.5;
+  Real tie_y = spec.position_ * details_.staff_space_ * 0.5;
   if (sign (curve_y - tie_y) != conf.dir_)
-    penalty += wrong_direction_offset_penalty_;
+    penalty += details_.wrong_direction_offset_penalty_;
+
+  penalty += details_.vertical_distance_penalty_factor_ * fabs (curve_y - tie_y);
+
+
+  Direction d = LEFT;
+  do
+    {
+      if (!spec.note_head_drul_[d])
+       continue;
+      
+      Interval head_x = spec.note_head_drul_[d]->extent (x_refpoint_, X_AXIS);
+      Real dist = head_x.distance (conf.attachment_x_[d]);
+      penalty += details_.horizontal_distance_penalty_factor_ * dist;
+    }
+  while  (flip (&d) != LEFT);
 
-  penalty += distance_penalty_factor_ * fabs (curve_y - tie_y);
   return penalty;
 }
 
-
 Real
 Tie_formatting_problem::score_configuration (Tie_configuration const &conf) const
 {
-  Real length_penalty_factor = 1.0;
-  Real min_length = 0.333;
-  Real staff_line_clearance = 0.1;
-  Real staff_line_collision_penalty = 5;
-  Real dot_collision_clearance = 0.25;
-  Real dot_collision_penalty = 10;
-  
-
   Real penalty = 0.0;
   Real length = conf.attachment_x_.length ();
-  if (length < min_length)
-    penalty += length_penalty_factor / max (0.01, length);
+
+  penalty += peak_around (0.5 * details_.min_length_, details_.min_length_, length);
 
   Real tip_pos = conf.position_ + conf.delta_y_ / 0.5 * details_.staff_space_;
   Real tip_y = tip_pos * details_.staff_space_ * 0.5;
@@ -357,19 +444,24 @@ Tie_formatting_problem::score_configuration (Tie_configuration const &conf) cons
   Real top_y = tip_y + conf.dir_ * height;
   Real top_pos = 2 * top_y / details_.staff_space_;
   Real round_top_pos = rint (top_pos);
-  if (fabs (top_pos - round_top_pos) < staff_line_clearance
-      && Staff_symbol_referencer::on_staffline (details_.staff_symbol_referencer_,
+  if (Staff_symbol_referencer::on_line (details_.staff_symbol_referencer_,
                                                int (round_top_pos))
       && Staff_symbol_referencer::staff_radius (details_.staff_symbol_referencer_) > top_y)
     {
-      penalty += staff_line_collision_penalty;      
+      penalty +=
+       details_.staff_line_collision_penalty_
+       * peak_around (0.1 * details_.center_staff_line_clearance_,
+                    details_.center_staff_line_clearance_,
+                    fabs (top_pos - round_top_pos));
     }
   
-  if (fabs (tip_pos - rint (tip_pos)) < staff_line_clearance
-      && Staff_symbol_referencer::on_staffline (details_.staff_symbol_referencer_,
-                                               int (rint (tip_pos))))
+  if (Staff_symbol_referencer::on_line (details_.staff_symbol_referencer_,
+                                       int (rint (tip_pos))))
     {
-      penalty += staff_line_collision_penalty;
+      penalty += details_.staff_line_collision_penalty_
+       * peak_around (0.1 * details_.tip_staff_line_clearance_,
+                      details_.tip_staff_line_clearance_,
+                      fabs (tip_pos - rint (tip_pos)));
     }
 
   if (!dot_x_.is_empty ())
@@ -386,37 +478,41 @@ Tie_formatting_problem::score_configuration (Tie_configuration const &conf) cons
               i != dot_positions_.end (); i ++)
            {
              int dot_pos = (*i);
-             if (fabs (dot_pos * details_.staff_space_ * 0.5 - y) < dot_collision_clearance)
-               {
-                 penalty += dot_collision_penalty;
-               }
+             penalty +=
+               details_.dot_collision_penalty_
+               * peak_around (.1 * details_.dot_collision_clearance_,
+                              details_.dot_collision_clearance_,
+                              fabs (dot_pos * details_.staff_space_ * 0.5 - y)); 
            }
-       }      
+       }
     }
   
   return penalty;
 }
 
 Tie_configuration
-Tie_formatting_problem::find_optimal_tie_configuration (int pos, Direction dir) const
+Tie_formatting_problem::find_optimal_tie_configuration (Tie_specification const &spec) const
 {
-  Link_array<Tie_configuration> confs;
+  vector<Tie_configuration*> confs;
+
+  int pos = spec.position_;
+  Direction dir = spec.manual_dir_;
 
   int region_size = 3;
   for (int i = 0; i < region_size; i ++)
     {
-      confs.push (generate_configuration (pos + i * dir, dir));
+      confs.push_back (generate_configuration (pos + i * dir, dir));
     }
 
-  Array<Real> scores;
+  vector<Real> scores;
 
   int best_idx = -1;
   Real best_score = 1e6;
-  for (int i = 0; i < confs.size (); i ++)
+  for (vsize i = 0; i < confs.size (); i ++)
     {
       Real score = 0.0;
       score += score_configuration (*confs[i]);
-      score += score_aptitude (*confs[i], pos);
+      score += score_aptitude (*confs[i], spec);
 
       if (score < best_score)
        {
@@ -426,7 +522,7 @@ Tie_formatting_problem::find_optimal_tie_configuration (int pos, Direction dir)
     }
 
   Tie_configuration best = *confs[best_idx];
-  for (int i = 0; i < confs.size (); i++)
+  for (vsize i = 0; i < confs.size (); i++)
     delete confs[i];
 
   return best;
@@ -439,6 +535,8 @@ Tie_specification::Tie_specification ()
   position_ = 0;
   manual_position_ = 0;
   manual_dir_ = CENTER;
+  note_head_drul_[LEFT] =
+    note_head_drul_[RIGHT] = 0;
 }
 
 
@@ -452,8 +550,8 @@ Tie_formatting_problem::score_ties_aptitude (Ties_configuration const &ties) con
       return infinity_f;
     }
 
-  for (int i = 0; i < ties.size (); i++)
-    score += score_aptitude (ties[i], specifications_[i].position_);
+  for (vsize i = 0; i < ties.size (); i++)
+    score += score_aptitude (ties[i], specifications_[i]);
 
   return score;
 }
@@ -468,20 +566,15 @@ Tie_formatting_problem::score_ties (Ties_configuration const &ties) const
 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++)
+  for (vsize 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++)
+  for (vsize i = 0; i < ties.size (); i++)
     {
       Bezier b (ties[i].get_transformed_bezier (details_));
        
@@ -491,22 +584,39 @@ Tie_formatting_problem::score_ties_configuration (Ties_configuration const &ties
       if (i)
        {
          if (edge <= last_edge)
-           score += tie_monotonicity_penalty;
+           score += details_.tie_column_monotonicity_penalty_;
          if (center <= last_center)
-           score += tie_monotonicity_penalty;
+           score += details_.tie_column_monotonicity_penalty_;
 
          score +=
-           tie_collision_penalty
-           * max (tie_collision_distance - fabs (center - last_center), 0.0) / tie_collision_distance;
+           details_.tie_tie_collision_penalty_ *
+           peak_around (0.1 * details_.tie_tie_collision_distance_,
+                        details_.tie_tie_collision_distance_,
+                        fabs (center - last_center));
          score +=
-           tie_collision_penalty
-           * max (tie_collision_distance - fabs (edge - last_edge), 0.0) / tie_collision_distance;
+           details_.tie_tie_collision_penalty_ *
+           peak_around (0.1 * details_.tie_tie_collision_distance_,
+                        details_.tie_tie_collision_distance_,
+                        fabs (edge - last_edge));
        }
 
       last_edge = edge;
       last_center = center;
     }
 
+
+  score +=
+    details_.outer_tie_length_symmetry_penalty_factor_
+    * fabs (ties[0].attachment_x_.length () - ties.back ().attachment_x_.length ());
+  
+  score +=
+    details_.outer_tie_vertical_distance_symmetry_penalty_factor_
+    * (fabs (specifications_[0].position_
+            - (ties[0].position_ * 0.5 * details_.staff_space_ + ties[0].delta_y_))
+       -
+       fabs (specifications_.back ().position_
+            - (ties.back ().position_ * 0.5 * details_.staff_space_ + ties.back ().delta_y_)));
+  
   return score;
 }
 
@@ -518,7 +628,7 @@ 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++)
+  for (vsize 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_)
@@ -527,7 +637,7 @@ Tie_formatting_problem::generate_ties_configuration (Ties_configuration const &t
            = (specifications_[i].manual_position_ - ties_config[i].position_)
            * 0.5 * details_.staff_space_;
        }
-      copy.push (*ptr);
+      copy.push_back (*ptr);
     }
   
   return copy;
@@ -537,26 +647,28 @@ Ties_configuration
 Tie_formatting_problem::generate_base_chord_configuration () 
 {
   Ties_configuration ties_config;
-  
-  
-  for (int i = 0;  i < specifications_.size (); i ++)
+  for (vsize 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.position_ = (int) my_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);
+       {
+         conf.position_ = specifications_[i].position_;
+       }
+      ties_config.push_back (conf);
     }
 
   set_ties_config_standard_directions (&ties_config);
+  for (vsize i = 0; i < ties_config.size (); i++)
+    if (!specifications_[i].manual_position_)
+      ties_config[i].position_ += ties_config[i].dir_;
 
   ties_config = generate_ties_configuration (ties_config);
   
@@ -567,22 +679,21 @@ Ties_configuration
 Tie_formatting_problem::generate_optimal_chord_configuration ()
 {
   Ties_configuration base = generate_base_chord_configuration ();
-  Array<Tie_configuration_variation> vars = get_variations (base);
+  vector<Tie_configuration_variation> vars = get_variations (base);
 
   Ties_configuration best = base;
-  Real best_score = score_ties_configuration (best);
+  Real best_score = score_ties (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++)
+  for (vsize 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));
+      Real score = score_ties (variant);
       if (score < best_score)
        {
          best = variant;
@@ -596,34 +707,34 @@ Tie_formatting_problem::generate_optimal_chord_configuration ()
 void
 Tie_formatting_problem::set_ties_config_standard_directions (Ties_configuration *tie_configs)
 {
-  if (tie_configs->is_empty ())
+  if (tie_configs->empty ())
     return ;
   
-  if (!tie_configs->elem (0).dir_)
-    tie_configs->elem_ref (0).dir_ = DOWN;
-  if (!tie_configs->top().dir_)
-    tie_configs->top().dir_ = UP;
+  if (!tie_configs->at (0).dir_)
+    tie_configs->at (0).dir_ = DOWN;
+  if (!tie_configs->back ().dir_)
+    tie_configs->back ().dir_ = UP;
 
   /*
     Seconds
    */
-  for (int i = 1; i < tie_configs->size (); i++)
+  for (vsize i = 1; i < tie_configs->size (); i++)
     {
-      Real diff = (tie_configs->elem (i-1).position_
-                  - tie_configs->elem (i).position_);
+      Real diff = (tie_configs->at (i-1).position_
+                  - tie_configs->at (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;
+         if (!tie_configs->at (i-1).dir_)
+           tie_configs->at (i-1).dir_ = DOWN;
+         if (!tie_configs->at (i).dir_)
+           tie_configs->at (i).dir_ = UP;
        }
     }
 
-  for (int i = 1; i < tie_configs->size() - 1; i++)
+  for (vsize i = 1; i < tie_configs->size() - 1; i++)
     {
-      Tie_configuration &conf = tie_configs->elem_ref (i);
+      Tie_configuration &conf = tie_configs->at (i);
       if (conf.dir_)
        continue;
 
@@ -642,14 +753,14 @@ Tie_configuration_variation::Tie_configuration_variation ()
   suggestion_ = 0;
 }
 
-Array<Tie_configuration_variation>
+vector<Tie_configuration_variation>
 Tie_formatting_problem::get_variations (Ties_configuration const &ties) 
 {
   Real center_distance_tolerance = 0.25;
   
-  Array<Tie_configuration_variation> vars;
+  vector<Tie_configuration_variation> vars;
   Real last_center = 0.0;
-  for (int i = 0; i < ties.size (); i++)
+  for (vsize i = 0; i < ties.size (); i++)
     {
       Bezier b (ties[i].get_transformed_bezier (details_));
        
@@ -663,55 +774,80 @@ Tie_formatting_problem::get_variations (Ties_configuration const &ties)
                {
                  Tie_configuration_variation var;
                  var.index_ = i;
-                 var.suggestion_ = get_configuration (ties[i].position_,
+                 var.suggestion_ = get_configuration (specifications_[i].position_
+                                                      - ties[i].dir_,
                                                       -ties[i].dir_);
 
-                 vars.push (var);
+                 vars.push_back (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_);
+                 var.suggestion_ = get_configuration (specifications_[i-1].position_
+                                                      - ties[i-1].dir_,
+                                                      - ties[i-1].dir_);
 
-                 vars.push (var);
+                 vars.push_back (var);
                }
            }
+         else if (dot_positions_.find (ties[i].position_) != dot_positions_.end ()
+                  && !specifications_[i].has_manual_position_)
+           {
+             Tie_configuration_variation var;
+             var.index_ = i;
+             var.suggestion_ = get_configuration (ties[i].position_  + ties[i].dir_,
+                                                  ties[i].dir_);
+             vars.push_back (var);
+           }
+         
        }
 
       last_center = center;
     }
 
+  /* TODO: switch off? */
+  Direction d = DOWN;
+  do
+    {
+      if (boundary (ties, d, 0).dir_ == d)
+       {
+         Tie_configuration_variation var;
+         var.index_ = (d == DOWN) ? 0 : ties.size () - 1;
+         var.suggestion_ = get_configuration (boundary (ties, d, 0).position_
+                                              + d, d);
+         vars.push_back (var);
+       }
+    }
+  while (flip (&d) !=  DOWN);
+
   return vars;
-  
 }
 
 void
 Tie_formatting_problem::set_manual_tie_configuration (SCM manual_configs)
 {
-  int k = 0;
+  vsize k = 0;
   for (SCM s = manual_configs;
-       scm_is_pair (s) && k < specifications_.size(); s = scm_cdr (s))
+       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)))
+      if (scm_is_pair (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_car (entry));
-       }
-         
+         Tie_specification &spec = specifications_[k];
+
+         if (scm_is_number (scm_car (entry)))
+           {
+             spec.has_manual_position_ = true;
+             spec.manual_position_ = scm_to_double (scm_car (entry));
+           }
+         if (scm_is_number (scm_cdr (entry)))
+           {
+             spec.has_manual_dir_ = true;
+             spec.manual_dir_ = Direction (scm_to_int (scm_cdr (entry)));
+           }
+       }         
       k ++;
     }
 }