]> git.donarmstrong.com Git - lilypond.git/blobdiff - lily/slur-scoring.cc
(recent_enough): interpret laziness
[lilypond.git] / lily / slur-scoring.cc
index d0ae6ba9160a1ad7c0349bf1ce682fa90e82c01a..6f91aa9b12895bb6c4cfb72265638d0b2acdfb1b 100644 (file)
 #include "warn.hh"
 #include "paper-column.hh"
 
+/*
+  TODO:
+
+  - curve around flag for y coordinate
+
+  - this file is a big mess, clean it up
+
+  
+  - short-cut: try a smaller region first.
+
+  - handle non-visible stems better.
+
+  - try to prune number of scoring criteria
+
+  - take encompass-objects more into account when determining
+  slur shape
+
+  - calculate encompass scoring directly after determining slur shape.
+
+  - optimize.
+
+*/
 
 struct Slur_score
 {
@@ -44,11 +66,6 @@ struct Slur_score
   }
 };
 
-/*
-  TODO: put in details property.,
-
-  use lowercase.
-*/
 struct Slur_score_parameters
 {
   int region_size_;
@@ -63,21 +80,19 @@ struct Slur_score_parameters
   Real max_slope_factor_;
   Real extra_object_collision_;
   Real accidental_collision_;
+  Real free_slur_distance_;
   Real free_head_distance_;
   Real extra_encompass_free_distance_;
+  Real edge_slope_exponent_;  
+  Real head_slur_distance_max_ratio_;
+  Real head_slur_distance_factor_;
+
+
+  
   Slur_score_parameters (Grob*);
 };
 
-/*
-  TODO:
 
-  - curve around flag for y coordinate
-  - better scoring.
-  - short-cut: try a smaller region first.
-  - collisions with accidentals
-  - collisions with articulations (staccato, portato, sforzato, ...)
-  - handle non-visible stems better.
-*/
 struct Encompass_info
 {
   Real x_;
@@ -89,6 +104,13 @@ struct Encompass_info
     stem_ = 0.0;
     head_ = 0.0;
   }
+  Real get_point (Direction dir)
+  {
+    Interval y;
+    y.add_point (stem_);
+    y.add_point (head_);
+    return y[dir];    
+  }
 };
 
 struct Bound_info
@@ -103,7 +125,7 @@ struct Bound_info
   Interval slur_head_extent_;
   Real neighbor_y_;
   Real staff_space_;
-
+  
   Bound_info ()
   {
     stem_ = 0;
@@ -222,6 +244,14 @@ init_score_param (Grob *me,
     = get_detail (details, ly_symbol2scm ("accidental-collision"));
   score_param->extra_encompass_free_distance_ 
     = get_detail (details, ly_symbol2scm ("extra-encompass-free-distance"));
+  score_param->head_slur_distance_factor_
+    = get_detail (details, ly_symbol2scm ("head-slur-distance-factor"));
+  score_param->head_slur_distance_max_ratio_
+    = get_detail (details, ly_symbol2scm ("head-slur-distance-max-ratio"));
+  score_param->free_slur_distance_
+    = get_detail (details, ly_symbol2scm ("free-slur-distance"));
+  score_param->edge_slope_exponent_
+    = get_detail (details, ly_symbol2scm ("edge-slope-exponent"));
 }
 
 
@@ -230,7 +260,7 @@ Slur_score_parameters::Slur_score_parameters(Grob *me)
   init_score_param (me, this);
 }
 
-/* HDIR indicates the direction for the slur.  */
+/* HDIR indicates which side (left or right) we are processing here.  */
 Real
 broken_trend_y (Grob *me, Grob **common, Direction hdir)
 {
@@ -271,7 +301,7 @@ broken_trend_y (Grob *me, Grob **common, Direction hdir)
       Direction vdir = get_grob_direction (me);
       Real neighbor_y
        = neighbor_col->extent (neighbor_common, Y_AXIS)
-       .linear_combination (int(neighbor_cols.size()==1 ? CENTER : vdir))
+       .linear_combination (int(vdir))
        - common_next_system->relative_coordinate (neighbor_common, Y_AXIS);
 
       Link_array<Grob> my_cols
@@ -383,7 +413,8 @@ Drul_array<Bound_info>
 get_bound_info (Spanner* me, Grob **common)
 {
   Drul_array<Bound_info> extremes;
-  Direction d = LEFT;
+
+  Direction d = RIGHT;
   Direction dir = get_grob_direction (me);
 
   do
@@ -415,19 +446,19 @@ get_bound_info (Spanner* me, Grob **common)
          extremes[d].staff_space_ = Staff_symbol_referencer
            ::staff_space (extremes[d].stem_);
        }
-      else
+      else if (d == RIGHT)
+       /*
+         right side anticipates on the next note.
+       */
        extremes[d].neighbor_y_ = broken_trend_y (me, common, d);
     }
-  while (flip (&d) != LEFT);
+  while (flip (&d) != RIGHT);
   return extremes;
 }
 
 void
 set_end_points (Grob *me)
 {
-
-  
-  
   Link_array<Grob> columns
     = Pointer_group_interface__extract_grobs (me, (Grob *) 0, "note-columns");
 
@@ -472,9 +503,8 @@ set_end_points (Grob *me)
                         &scores);
 
   Real opt = 1e6;
-  int opt_idx = 0;
-  // why backwards?
-  for (int i = scores.size (); i--;)
+  int opt_idx = -1;
+  for (int i = 0; i < scores.size (); i++)
     {
       if (scores[i].score_ < opt)
        {
@@ -486,7 +516,7 @@ set_end_points (Grob *me)
 #if DEBUG_SLUR_QUANTING
   SCM inspect_quants = me->get_property ("inspect-quants");
   if (to_boolean (me->get_paper ()
-                 ->lookup_variable (ly_symbol2scm ("debug-slur-quanting")))
+                 ->lookup_variable (ly_symbol2scm ("debug-slur-scoring")))
       && ly_c_pair_p (inspect_quants))
     {
       Drul_array<Real> ins = ly_scm2interval (inspect_quants);
@@ -517,7 +547,7 @@ set_end_points (Grob *me)
     {
       Offset o = b.control_[i]
        - Offset (me->relative_coordinate (common[X_AXIS], X_AXIS),
-                 me->relative_coordinate (common[Y_AXIS], Y_AXIS));
+                 me->relative_coordinate (common[Y_AXIS], Y_AXIS));
       controls = scm_cons (ly_offset2scm (o), controls);
     }
   me->set_property ("control-points", controls);
@@ -548,7 +578,7 @@ get_y_attachment_range (Spanner*me,
               >? (dir * base_attachment[-d][Y_AXIS]));
        }
       else
-       end_ys[d] = extremes[d].neighbor_y_ + score_param->region_size_ * dir;
+       end_ys[d] = base_attachment[d][Y_AXIS] + score_param->region_size_ * dir;
     }
   while (flip (&d) != LEFT);
 
@@ -580,7 +610,7 @@ get_base_attachments (Spanner *me,
   Drul_array<Offset> base_attachment;
   Real staff_space = Staff_symbol_referencer::staff_space ((Grob *) me);
   Direction dir = get_grob_direction (me);
-  Direction d = LEFT;
+  Direction d = RIGHT;
   do
     {
       Grob *stem = extremes[d].stem_;
@@ -589,11 +619,23 @@ get_base_attachments (Spanner *me,
       Real x, y;
       if (!extremes[d].note_column_)
        {
-         y = extremes[d].neighbor_y_;
-         if (d== RIGHT)
-           x = extremes[d].bound_->extent (common[X_AXIS], X_AXIS)[d];
+         if (d == RIGHT)
+           {
+             y = extremes[d].neighbor_y_;
+             x = extremes[d].bound_->extent (common[X_AXIS], X_AXIS)[d];
+           }
          else
-           x = me->get_broken_left_end_align ();
+           {
+             x = me->get_broken_left_end_align ();
+             if (extremes[RIGHT].bound_ == columns[0])
+               {
+                 y = base_attachment[RIGHT][Y_AXIS];
+               }
+             else
+               {
+                 y = columns[0]->extent (common[Y_AXIS], Y_AXIS)[dir]; 
+               }
+           }
        }
       else
        {
@@ -635,7 +677,7 @@ get_base_attachments (Spanner *me,
        }
       base_attachment[d] = Offset (x, y);
 
-    } while (flip (&d) != LEFT);
+    } while (flip (&d) != RIGHT);
 
   return base_attachment;
 }
@@ -651,14 +693,15 @@ generate_curves (Grob *me, Grob **common,
   (void) extremes;
   Real staff_space = Staff_symbol_referencer::staff_space ((Grob *) me);
   Real r_0 = robust_scm2double (me->get_property ("ratio"), 0.33);
-  Real h_inf = staff_space * ly_scm2double (me->get_property ("height-limit"));
+  Real h_inf = staff_space * scm_to_double (me->get_property ("height-limit"));
   for (int i = 0; i < scores->size(); i++)
     {
-      Bezier bez= get_bezier (me, 
+      Bezier bez = get_bezier (me, 
                              common,
                              score_param,
                              extremes,
                              (*scores)[i].attachment_, r_0, h_inf);
+      
       bez = avoid_staff_line (me, common, extremes, bez);
       (*scores)[i].attachment_[LEFT] = bez.control_[0];
       (*scores)[i].attachment_[RIGHT] = bez.control_[3];
@@ -704,13 +747,9 @@ avoid_staff_line (Grob *me, Grob **common,
            * 5 * thick;
        
          Real dy = (newp - p) * staff_space / 2.0;
-#if 0
-         bez.translate (Offset (0, dy));
-#else
+         
          bez.control_[1][Y_AXIS] += dy;
          bez.control_[2][Y_AXIS] += dy;
-       
-#endif
        }
     }
   return bez;
@@ -809,9 +848,19 @@ enumerate_attachments (Grob *me, Grob *common[],
       
       os[LEFT][Y_AXIS] += dir * staff_space / 2;
     }
+
+  assert (scores.size() > 0);
   return scores;
 }
 
+inline Real 
+linear_interpolate (Real x, Real x1, Real x2,  Real y1, Real  y2)
+{
+  return (x2 - x) / (x2 - x1) * y1 +
+    (x - x1) / (x2 - x1) * y2 ;
+}
+
+
 void
 score_encompass (Grob *me, Grob *common[],
                 Slur_score_parameters *score_param,
@@ -833,8 +882,16 @@ score_encompass (Grob *me, Grob *common[],
 
   for (int i = 0; i < scores->size (); i++)
     {
-      Bezier const &bez (scores->elem (i).curve_);
+      Slur_score &configuration = scores->elem_ref (i);
+      Bezier const &bez (configuration.curve_);
       Real demerit = 0.0;
+
+      /*
+       Distances for heads that are between slur and line between
+       attachment points.
+       */
+      Array<Real> convex_head_distances;
+      Array<Real> edge_distances;
       for (int j = 0; j < infos.size(); j++)
        {
          Real x = infos[j].x_;
@@ -843,8 +900,16 @@ score_encompass (Grob *me, Grob *common[],
          bool r_edge = j==infos.size()-1;
          bool edge =  l_edge || r_edge;
 
-         if (!(x < scores->elem (i).attachment_[RIGHT][X_AXIS]
-               && x > scores->elem (i).attachment_[LEFT][X_AXIS]))
+
+         if (edge)
+         {
+           edge_distances.push (fabs (configuration.attachment_[l_edge ? LEFT : RIGHT][Y_AXIS]
+                                      - infos[j].get_point (dir)));
+         } 
+         
+         
+         if (!(x < configuration.attachment_[RIGHT][X_AXIS]
+               && x > configuration.attachment_[LEFT][X_AXIS]))
            continue;
        
          Real y = bez.get_other_coordinate (X_AXIS, x);
@@ -852,7 +917,10 @@ score_encompass (Grob *me, Grob *common[],
            {
              Real head_dy = (y - infos[j].head_);
              if (dir * head_dy < 0)
-               demerit += score_param->head_encompass_penalty_;
+               {
+                 demerit += score_param->head_encompass_penalty_;
+                 convex_head_distances.push (0.0); 
+               }
              else
                {
                  Real hd = (head_dy)
@@ -860,9 +928,29 @@ score_encompass (Grob *me, Grob *common[],
                    : score_param->head_encompass_penalty_;
                  hd = (hd >? 0)<? score_param->head_encompass_penalty_;
 
-                 demerit += hd;        
+                 demerit += hd;
+               }
+
+             Real line_y = linear_interpolate (x,
+                                               configuration.attachment_[RIGHT][X_AXIS],
+                                               configuration.attachment_[LEFT][X_AXIS],
+                                               configuration.attachment_[RIGHT][Y_AXIS],
+                                               configuration.attachment_[LEFT][Y_AXIS]);
+
+             if ( 1 ) // dir * infos[j].get_point (dir) > dir *line_y )
+               {
+                 
+                 Real closest =
+                   dir * (dir * infos[j].get_point (dir)
+                          >? dir *line_y
+                          );
+                 Real d = fabs(closest - y);
+             
+                 convex_head_distances.push (d);
                }
-           }   
+           }
+         
+       
 
          if (dir * (y - infos[j].stem_) < 0)
            {
@@ -888,14 +976,75 @@ score_encompass (Grob *me, Grob *common[],
            }
        }
 
+      Real variance_penalty = 0.0;
+
+      if (convex_head_distances.size())
+       {
+         Real avg_distance = 0.0;
+         Real min_dist = infinity_f;
+         for (int j = 0; j < convex_head_distances.size(); j++)
+           {
+             min_dist = min_dist <? convex_head_distances[j];
+             avg_distance += convex_head_distances[j];
+           }
+
+         /*
+           For slurs over 3 or 4 heads, the average distance is not a
+           good normalizer.
+          */
+         int n =  convex_head_distances.size();
+         if (convex_head_distances.size() <= 2)
+           {
+             //              Real min_edge_dist = 1e6;
+             for (int j = 0; j < edge_distances.size(); j++)
+               {
+                 avg_distance += edge_distances[j];
+                 n++;
+               }
+
+           }
+
+         /*
+           TODO: maybe it's better to use (avgdist - mindist)*factor
+           as penalty.
+          */
+         avg_distance /= n;
+         variance_penalty = score_param->head_slur_distance_max_ratio_;
+         if (min_dist > 0.0)
+           variance_penalty = ((avg_distance / (min_dist  +score_param->free_head_distance_)) - 1.0)
+             <? variance_penalty;
+      
+         variance_penalty *= score_param->head_slur_distance_factor_;
+       }
 #if DEBUG_SLUR_QUANTING
       (*scores)[i].score_card_ += to_string ("C%.2f", demerit);
+      (*scores)[i].score_card_ += to_string ("D%.2f", variance_penalty);
 #endif
 
-      (*scores)[i].score_ += demerit;
+      (*scores)[i].score_ += demerit + variance_penalty;
     }
 }
 
+struct Extra_collision_info
+{
+  Real idx_;
+  Box extents_;
+  Real penalty_;
+
+  Extra_collision_info (Real idx, Interval x, Interval y, Real p)
+  {
+    idx_ = idx;
+    extents_[X_AXIS] = x;
+    extents_[Y_AXIS] = y;
+    penalty_ = p;    
+  }
+  Extra_collision_info ()
+  {
+    idx_ = 0.0;
+    penalty_ = 0.;
+  }
+};
+
 void
 score_extra_encompass (Grob *me, Grob *common[],
                       Slur_score_parameters *score_param,
@@ -906,6 +1055,8 @@ score_extra_encompass (Grob *me, Grob *common[],
   (void) base_attach;
   (void) extremes;
 
+  Spanner *me_spanner = dynamic_cast<Spanner*> (me);
+  
   Link_array<Grob> encompasses
     = Pointer_group_interface__extract_grobs (me, (Grob *)0,
                                              "encompass-objects");
@@ -913,24 +1064,44 @@ score_extra_encompass (Grob *me, Grob *common[],
   Real lt =  me->get_paper ()->get_dimension (ly_symbol2scm ("linethickness"));
   Real thick = robust_scm2double (me->get_property ("thickness"), 1.0) * lt;
 
-  Array<Real> xidxs;
-  Array<Interval> yexts;
-  Array<Interval> xexts;
-
+  Array<Extra_collision_info> collision_infos;
   for (int i = encompasses.size (); i--; )
     {
       if (Slur::has_interface (encompasses[i]))
        {
-         Grob * small_slur = encompasses[i];
+         Spanner * small_slur = dynamic_cast<Spanner*> (encompasses[i]);
          Bezier b = Slur::get_curve (small_slur);
 
-         Offset z = b.curve_point (0.5);
-         z += Offset (small_slur->relative_coordinate (common[X_AXIS], X_AXIS),
-                      small_slur->relative_coordinate (common[Y_AXIS], Y_AXIS));
-
-         xexts.push (Interval (z[X_AXIS], z[X_AXIS]));
-         xidxs.push (0.0);
-         yexts.push (z[Y_AXIS] + thick * Interval (-0.5, 0.5));
+         Offset relative (small_slur->relative_coordinate (common[X_AXIS], X_AXIS),
+                          small_slur->relative_coordinate (common[Y_AXIS], Y_AXIS));
+
+         for (int k = 0; k < 3; k++)
+         {
+           Direction hdir =  Direction (k /2 - 1);
+
+           /*
+             Only take bound into account if small slur starts
+             together with big slur.
+            */
+           if (hdir && small_slur->get_bound (hdir) != me_spanner->get_bound (hdir))
+             continue;
+             
+
+           Offset z = b.curve_point ( k / 2.0);
+           z += relative;
+
+           Interval yext;
+           yext.set_full();
+           yext[dir] = z[Y_AXIS] + dir * thick * 1.0;
+
+           Interval xext(-1, 1);
+           xext = xext *(thick*2) + z[X_AXIS];
+           Extra_collision_info info (k - 1.0, 
+                                      xext,
+                                      yext,
+                                      score_param->extra_object_collision_);
+           collision_infos.push (info);
+         }
        }
       else
        {
@@ -939,9 +1110,10 @@ score_extra_encompass (Grob *me, Grob *common[],
          Interval ye = g->extent (common[Y_AXIS], Y_AXIS);
 
          Real xp = 0.0;
-
+         Real penalty = score_param->extra_object_collision_;
          if (Accidental_interface::has_interface (g))
            {
+             penalty = score_param->accidental_collision_;
              /* Begin copy accidental.cc */
              bool parens = false;
              if (to_boolean (g->get_property ("cautionary")))
@@ -952,12 +1124,12 @@ score_extra_encompass (Grob *me, Grob *common[],
        
              SCM accs = g->get_property ("accidentals");
              SCM scm_style = g->get_property ("style");
-             if (!ly_c_symbol_p (scm_style)
+             if (!scm_is_symbol (scm_style)
                  && !parens
                  && scm_ilength (accs) == 1)
                {
                  /* End copy accidental.cc */
-                 switch (ly_scm2int (ly_car (accs)))
+                 switch (scm_to_int (ly_car (accs)))
                    {
                    case FLAT:
                    case DOUBLE_FLAT:
@@ -973,57 +1145,55 @@ score_extra_encompass (Grob *me, Grob *common[],
                }
            }
 
-         xidxs.push (xp);
          ye.widen (thick * 0.5);
-         yexts.push (ye);
-         xexts.push (xe);
+         xe.widen (thick * 1.0);
+         Extra_collision_info info (xp, xe, ye,  penalty);
+         collision_infos.push (info);
        }
     }
   for (int i = 0; i < scores->size (); i++)
     {
       Real demerit = 0.0;
-      for (int j = 0; j < xidxs.size(); j++)
+      for (int j = 0; j < collision_infos.size(); j++)
        {
-         Drul_array<Offset> at = scores->elem (i).attachment_;
-         Interval slur_wid (at[LEFT][X_AXIS], at[RIGHT][X_AXIS]);
+         Drul_array<Offset> attachment = scores->elem (i).attachment_;
+         Interval slur_wid (attachment[LEFT][X_AXIS], attachment[RIGHT][X_AXIS]);
 
          /*
            to prevent numerical inaccuracies in
            Bezier::get_other_coordinate().
-          */
-         slur_wid.widen (- 0.5 * thick);
-         Real x = xexts[j].linear_combination (xidxs[j]);
+         */
+         Direction d = LEFT;
+         bool found = false;
          Real y = 0.0;
-         if (!slur_wid.contains (x))
-           {   
-             Direction contains_dir = CENTER;
-             Direction d = LEFT;
-             do
+         
+         do
+           {
+             if (collision_infos[j].extents_[X_AXIS].contains (attachment[d][X_AXIS]))
                {
-                 if (xexts[j].contains (at[d][X_AXIS]))
-                   contains_dir = d; 
+                 y = attachment[d][Y_AXIS];
+                 found = true;
                }
-             while (flip (&d) != LEFT);
-
-             if (!contains_dir)
-               continue;
-             else
-               y = at[contains_dir][Y_AXIS];
            }
-         else
+         while (flip (&d) != LEFT);
+
+         if (!found)
            {
+             
+             Real x = collision_infos[j].extents_[X_AXIS]
+               .linear_combination (collision_infos[j].idx_);
+             if (!slur_wid.contains (x))
+               continue;
+             
              y = scores->elem (i).curve_.get_other_coordinate (X_AXIS, x);
            }
 
-         Real collision_demerit = 
-              (Accidental_interface::has_interface (encompasses[j]))
-           ? score_param->accidental_collision_
-           : score_param->extra_object_collision_;
-         
-         Real dist = yexts[j].distance (y);
+         Real dist = collision_infos[j].extents_[Y_AXIS].distance (y);
          demerit +=
            fabs (0 >? (score_param->extra_encompass_free_distance_ - dist)) /
-           score_param->extra_encompass_free_distance_  * collision_demerit;
+           score_param->extra_encompass_free_distance_
+           * collision_infos[j].penalty_;
        }
 #if DEBUG_SLUR_QUANTING
       (*scores)[i].score_card_ += to_string ("X%.2f", demerit);
@@ -1032,6 +1202,10 @@ score_extra_encompass (Grob *me, Grob *common[],
     }
 }
 
+/*
+  TODO: should make edge penalties dependent on the direction that the
+  slur-end is pointing.
+ */
 void
 score_edges (Grob *me, Grob *common[],
             Slur_score_parameters * score_param,
@@ -1045,9 +1219,12 @@ score_edges (Grob *me, Grob *common[],
   for (int i = 0; i < scores->size (); i++)
     {
       Direction d = LEFT;
+      Slur_score &config = scores->elem_ref (i);
+      Offset dz = config.attachment_[RIGHT] - config.attachment_[LEFT];
+      Real slope = dz[Y_AXIS] / dz[X_AXIS];
       do
        {
-         Real y = scores->elem (i).attachment_[d][Y_AXIS];
+         Real y = config.attachment_[d][Y_AXIS];
          Real dy = fabs (y - base_attach[d][Y_AXIS]);
        
          Real factor = score_param->edge_attraction_factor_;
@@ -1057,7 +1234,10 @@ score_edges (Grob *me, Grob *common[],
              && !Stem::get_beaming (extremes[d].stem_, -d)
              )
            demerit /= 5;
-       
+
+         demerit *= exp (dir * d * slope
+                         * score_param->edge_slope_exponent_ );
+         
          (*scores)[i].score_ += demerit;
 #if DEBUG_SLUR_QUANTING
          (*scores)[i].score_card_ += to_string ("E%.2f", demerit);
@@ -1177,13 +1357,14 @@ get_bezier (Grob *me,
            Slur_score_parameters *score_param,
            Drul_array<Bound_info> extremes,       
            Drul_array<Offset> attachments,
-           Real r_0, Real h_inf)
+           Real r_0, Real h_inf
+           )
 {
   Link_array<Grob> encompasses
     = Pointer_group_interface__extract_grobs (me, (Grob *)0, "note-columns");
   Direction dir = get_grob_direction (me);
 
-  Array<Offset> avoid;
+  Array<Offset> avoid;  
   for (int i = 0; i < encompasses.size(); i++)
     {
       if (extremes[LEFT].note_column_ == encompasses[i]
@@ -1196,6 +1377,22 @@ get_bezier (Grob *me,
       
       avoid.push (Offset (inf.x_,  y + dir * score_param->free_head_distance_));
     }
+
+  Link_array<Grob> extra_encompasses
+    = Pointer_group_interface__extract_grobs (me, (Grob *)0, "encompass-objects");
+  for (int i = 0;  i < extra_encompasses.size (); i++)
+    if (Slur::has_interface (extra_encompasses[i]))
+      {
+       Grob * small_slur = extra_encompasses[i];
+       Bezier b = Slur::get_curve (small_slur);
+
+       Offset z = b.curve_point (0.5);
+       z += Offset (small_slur->relative_coordinate (common[X_AXIS], X_AXIS),
+                    small_slur->relative_coordinate (common[Y_AXIS], Y_AXIS));
+
+       z[Y_AXIS] += dir * score_param->free_slur_distance_;
+       avoid.push (z);
+      }
   
   Offset dz = attachments[RIGHT]- attachments[LEFT];;
   Offset dz_unit = dz;