]> git.donarmstrong.com Git - lilypond.git/blobdiff - lily/slur.cc
release: 1.3.101
[lilypond.git] / lily / slur.cc
index dc9dc15406395d9788f53391d814ac18e27e0b37..b7b0c687695c594ba27a696fbb9dd4d5e77459b1 100644 (file)
 
   source file of the GNU LilyPond music typesetter
 
-  (c) 1996,  1997--1999, 1998 Han-Wen Nienhuys <hanwen@cs.uu.nl>
+  (c) 1996--2000 Han-Wen Nienhuys <hanwen@cs.uu.nl>
     Jan Nieuwenhuizen <janneke@gnu.org>
 */
 
 /*
   [TODO]
-    * URG: share code with tie
-    * begin and end should be treated as a Script.
-    * damping
-    * slur from notehead to stemend: c''()b''
+    * fix broken interstaff slurs
+    * should avoid stafflines with horizontal part.
+    * begin and end should be treated as a/acknowledge Scripts.
+    * smart changing of endings
+    * smart changing of (Y-?)offsets to avoid ugly beziers
+       (along-side-stem)
  */
 
+#include "directional-element-interface.hh"
+#include "group-interface.hh"
 #include "slur.hh"
-#include "scalar.hh"
 #include "lookup.hh"
 #include "paper-def.hh"
 #include "note-column.hh"
 #include "stem.hh"
-#include "p-col.hh"
+#include "paper-column.hh"
 #include "molecule.hh"
 #include "debug.hh"
-#include "box.hh"
-#include "bezier.hh"
-#include "encompass-info.hh"
+#include "slur-bezier-bow.hh"
 #include "main.hh"
+#include "group-interface.hh"
+#include "staff-symbol-referencer.hh"
+#include "spanner.hh"
 
 
-Slur::Slur ()
+void
+Slur::set_interface (Score_element*me)
 {
+  me->set_elt_property ("attachment", gh_cons (SCM_BOOL_F, SCM_BOOL_F));
+  me->set_interface (ly_symbol2scm ("slur-interface"));
 }
 
 void
-Slur::add_column (Note_column*n)
+Slur::add_column (Score_element*me, Score_element*n)
 {
-  if (!n->head_l_arr_.size ())
-    warning (_ ("Putting slur over rest."));
-  encompass_arr_.push (n);
-  n->stem_l_->slur_l_ = this;
-  add_dependency (n);
+  if (!gh_pair_p (n->get_elt_property ("note-heads")))
+    warning (_ ("Putting slur over rest.  Ignoring."));
+  else
+    {
+      Pointer_group_interface::add_element (me, "note-columns",n);
+      me->add_dependency (n);
+    }
+
+  add_bound_item (dynamic_cast<Spanner*> (me), dynamic_cast<Item*>(n));
 }
 
 void
-Slur::set_default_dir ()
+Slur::de_uglyfy (Score_element*me, Slur_bezier_bow* bb, Real default_height)
 {
-  dir_ = DOWN;
-  for (int i=0; i < encompass_arr_.size (); i ++) 
+  Real length = bb->curve_.control_[3][X_AXIS] ; 
+  Real ff = bb->fit_factor ();
+  for (int i = 1; i < 3; i++)
     {
-      if (encompass_arr_[i]->dir_ < 0) 
+      Real ind = abs (bb->curve_.control_[(i-1)*3][X_AXIS]
+                     - bb->curve_.control_[i][X_AXIS]) / length;
+      Real h = bb->curve_.control_[i][Y_AXIS] * ff / length;
+
+      Real f = default_height / length;
+      SCM up = me->get_elt_property ("de-uglify-parameters");
+      
+      Real c1 = gh_scm2double (gh_car (up));
+      Real c2 = gh_scm2double (gh_cadr (up));
+      Real c3 = gh_scm2double (gh_caddr (up)); 
+      
+      if (h > c1 * f)
+       {
+         h = c1 * f; 
+       }
+      else if (h > c2 + c3 * ind)
        {
-         dir_ = UP;
+         h = c2 + c3 * ind; 
+       }
+      
+      bb->curve_.control_[i][Y_AXIS] = h * length;
+    } 
+
+  bb->curve_.assert_sanity ();
+}
+
+Direction
+Slur::get_default_dir (Score_element*me) 
+{
+  Link_array<Score_element> encompass_arr =
+    Pointer_group_interface__extract_elements (me, (Score_element*)0, "note-columns");
+  
+  Direction d = DOWN;
+  for (int i=0; i < encompass_arr.size (); i ++) 
+    {
+      if (Note_column::dir (encompass_arr[i]) < 0) 
+       {
+         d = UP;
          break;
        }
     }
+  return d;
 }
 
-void
-Slur::do_add_processing ()
+
+MAKE_SCHEME_CALLBACK (Slur, after_line_breaking,1);
+SCM
+Slur::after_line_breaking (SCM smob)
 {
-  set_bounds (LEFT, encompass_arr_[0]);    
-  if (encompass_arr_.size () > 1)
-    set_bounds (RIGHT, encompass_arr_.top ());
-}
+  Score_element *me = unsmob_element (smob);
+  if (!scm_ilength (me->get_elt_property ("note-columns")))
+    {
+      me->suicide ();
+      return SCM_UNSPECIFIED;
+    }
+  set_extremities (me);
+  set_control_points (me);
+  return SCM_UNSPECIFIED;
+} 
+
 
 void
-Slur::do_pre_processing ()
+Slur::check_slope (Score_element *me)
 {
-  // don't set directions
+  /*
+    Avoid too steep slurs.
+   */
+  SCM s = me->get_elt_property ("slope-limit");
+  if (gh_number_p (s))
+    {
+      Array<Offset> encompass = get_encompass_offset_arr (me);
+      Drul_array<Offset> attachment;
+      attachment[LEFT] = encompass[0];
+      attachment[RIGHT] = encompass.top ();
+
+      Real dx = attachment[RIGHT][X_AXIS] - attachment[LEFT][X_AXIS];
+      Real dy = attachment[RIGHT][Y_AXIS] - attachment[LEFT][Y_AXIS];
+      if (!dx)
+       return;
+      
+      Real slope = slope = abs (dy / dx);
+
+      Real limit = gh_scm2double (s);
+
+      if (slope > limit)
+       {
+         Real staff_space = Staff_symbol_referencer::staff_space ((Score_element*)me);
+         Direction dir = (Direction)gh_scm2int (me->get_elt_property ("direction"));
+         Direction d = (Direction)(- dir * (sign (dy)));
+         SCM a = me->get_elt_property ("attachment-offset");
+         Drul_array<Offset> o;
+         o[LEFT] = ly_scm2offset (index_cell (a, LEFT));
+         o[RIGHT] = ly_scm2offset (index_cell (a, RIGHT));
+         o[d][Y_AXIS] -= (limit - slope) * dx * dir / staff_space;
+         //o[d][Y_AXIS] = attachment[-d][Y_AXIS] + (dx * limit * dir / staff_space);
+         me->set_elt_property ("attachment-offset",
+                               gh_cons (ly_offset2scm (o[LEFT]),
+                                        ly_offset2scm (o[RIGHT])));
+       }
+    }
 }
 
 void
-Slur::do_substitute_element_pointer (Score_element*o, Score_element*n)
+Slur::set_extremities (Score_element *me)
 {
-  int i;
-  while ((i = encompass_arr_.find_i (dynamic_cast<Note_column *> (o))) >=0) 
+  if (!Directional_element_interface::get (me))
+    Directional_element_interface::set (me, get_default_dir (me));
+
+  Direction dir = LEFT;
+  do 
     {
-      if (n)
-       encompass_arr_[i] = dynamic_cast<Note_column *> (n);
-      else
-       encompass_arr_.del (i);
+      if (!gh_symbol_p (index_cell (me->get_elt_property ("attachment"), dir)))
+       {
+         for (SCM s = me->get_elt_property ("extremity-rules");
+              s != SCM_EOL; s = gh_cdr (s))
+           {
+             SCM r = gh_call2 (gh_caar (s), me->self_scm (),
+                                gh_int2scm ((int)dir));
+             if (r != SCM_BOOL_F)
+               {
+                 index_set_cell (me->get_elt_property ("attachment"), dir,
+                                 gh_cdar (s));
+                 break;
+               }
+           }
+       }
     }
-}
+  while (flip (&dir) != LEFT);
 
-static int 
-Note_column_compare (Note_column *const&n1 , Note_column* const&n2)
-{
-  return Item::left_right_compare (n1, n2);
+  check_slope (me);
 }
 
-static bool
-broken_edge_b (Slur*s, Drul_array<Note_column*>& extrema, Direction dir)
+
+Real
+Slur::get_first_notecolumn_y (Score_element *me, Direction dir)
 {
-  return extrema[dir] != s->spanned_drul_[dir];
+  Score_element *col = dir == LEFT
+    ? unsmob_element (gh_car (scm_reverse (me->get_elt_property
+                                          ("note-columns"))))
+    : unsmob_element
+    (gh_car (me->get_elt_property ("note-columns")));
+  
+  Score_element *common[] =
+  {
+    0,
+    me->common_refpoint (col, Y_AXIS)
+  };
+  Real y;
+  if (col == ((Spanner*)me)->get_bound (dir))
+    {
+      y = get_attachment (me, dir, common)[Y_AXIS];
+    }
+  else
+    {
+      y = encompass_offset (me, col, common)[Y_AXIS]
+       - me->relative_coordinate (common[Y_AXIS], Y_AXIS); 
+    }
+  return y;
 }
 
-static bool
-normal_edge_b (Slur*s, Drul_array<Note_column*>& extrema, Direction dir)
+Offset
+Slur::broken_trend_offset (Score_element *me, Direction dir)
 {
-  return !broken_edge_b (s, extrema, dir)
-    && extrema[dir]->stem_l_
-    && !extrema[dir]->stem_l_->transparent_b_ 
-    && extrema[dir]->head_l_arr_.size ();
+  /*
+    A broken slur should maintain the same vertical trend
+    the unbroken slur would have had.
+  */
+  Offset o;
+  if (Spanner *mother =  dynamic_cast<Spanner*> (me->original_l_))
+    {
+      for (int i = dir == LEFT ? 0 : mother->broken_into_l_arr_.size () - 1;
+          dir == LEFT ? i < mother->broken_into_l_arr_.size () : i > 0;
+          dir == LEFT ? i++ : i--)
+       {
+         if (mother->broken_into_l_arr_[i - dir] == me)
+           {
+             Score_element *neighbour = mother->broken_into_l_arr_[i];
+             if (dir == RIGHT)
+               neighbour->set_elt_property ("direction",
+                                            me->get_elt_property ("direction"));
+             Real neighbour_y = get_first_notecolumn_y (neighbour, dir);
+             Real y = get_first_notecolumn_y (me, -dir);
+             int neighbour_cols = scm_ilength (neighbour->get_elt_property ("note-columns"));
+             int cols = scm_ilength (me->get_elt_property ("note-columns"));
+             o = Offset (0, (y*neighbour_cols + neighbour_y*cols) /
+                         (cols + neighbour_cols));
+             break;
+           }
+       }
+    }
+  return o;
 }
 
-void
-Slur::do_post_processing ()
+Offset
+Slur::get_attachment (Score_element *me, Direction dir,
+                     Score_element **common) 
 {
-  encompass_arr_.sort (Note_column_compare);
-  if (!dir_)
-    set_default_dir ();
-
-  Real interline_f = paper ()->interline_f ();
-  Real internote_f = interline_f / 2;
-  // URG
-  Real notewidth_f = paper ()->note_width () * 0.8;
-
-  /* 
-   [OSU]: slur and tie placement
-
-   slurs:
-   * x = centre of head (upside-down: inner raakpunt stem) - d * gap
-
-   * y = length < 5ss : horizontal raakpunt + d * 0.25 ss
-     y = length >= 5ss : y next interline - d * 0.25 ss
-     --> height <= 5 length ?? we use <= 3 length, now...
-   */
+  SCM s = me->get_elt_property ("attachment");
+  if (!gh_symbol_p (index_cell (s, dir)))
+    {
+      set_extremities (me);
+      s = me->get_elt_property ("attachment");
+    }
+  SCM a = dir == LEFT ? gh_car (s) : gh_cdr (s);
+  Spanner*sp = dynamic_cast<Spanner*>(me);
+  String str = ly_symbol2string (a);
+  Real staff_space = Staff_symbol_referencer::staff_space ((Score_element*)me);
+  Real hs = staff_space / 2.0;
+  Offset o;
   
-  Real gap_f = paper ()->get_var ("slur_x_gap");
-
-  Drul_array<Note_column*> extrema;
-  extrema[LEFT] = encompass_arr_[0];
-  extrema[RIGHT] = encompass_arr_.top ();
-
-  Direction d=LEFT;
-  do 
+  Score_element *stem = 0;
+  if (Note_column::has_interface (sp->get_bound (dir)))
     {
-      if (broken_edge_b (this, extrema, d))
+      Score_element * n =sp->get_bound (dir);
+      if ((stem = Note_column::stem_l (n)))
        {
-         // ugh -- check if needed
-         dx_f_drul_[d] = -d 
-           *(spanned_drul_[d]->extent (X_AXIS).length () - 0.5 * notewidth_f);
 
-         // prebreak
-         if (d == RIGHT)
+         if (str == "head")
            {
-             dx_f_drul_[LEFT] = spanned_drul_[LEFT]->extent (X_AXIS).length ();
-
-             // urg -- check if needed
-             if (encompass_arr_.size () > 1)
-               dx_f_drul_[RIGHT] += notewidth_f;
+             o = Offset (0, Stem::head_positions (stem)
+                         [Directional_element_interface::get (me)] * hs);
+             /*
+               Default position is centered in X, on outer side of head Y
+              */
+             o += Offset (0.5 * n->extent (n,X_AXIS).length (),
+                          0.5 * staff_space
+                          * Directional_element_interface::get (me));
            }
-       }
-      /*
-        normal slur
-       */
-      else if (normal_edge_b (this, extrema, d))
-        {
-         Real notewidth_f = extrema[d]->extent (X_AXIS).length ();
-         dy_f_drul_[d] = (int)rint (extrema[d]->stem_l_-> extent (Y_AXIS)[dir_]);
-         dx_f_drul_[d] += 0.5 * notewidth_f - d * gap_f;
-         if (dir_ == extrema[d]->stem_l_->dir_)
+         else if (str == "alongside-stem")
            {
-             if (dir_ == d)
-               dx_f_drul_[d] += 0.5 * (dir_ * d) * d * notewidth_f;
-             else
-               dx_f_drul_[d] += 0.25 * (dir_ * d) * d * notewidth_f;
+             o = Offset (0, Stem::chord_start_f (stem));
+             /*
+               Default position is on stem X, on outer side of head Y
+              */
+             o += Offset (n->extent (n,X_AXIS).length ()
+                          * (1 + Stem::get_direction (stem)),
+                          0.5 * staff_space
+                          * Directional_element_interface::get (me));
+           }
+         else if (str == "stem")
+           {
+             o = Offset (0, Stem::stem_end_position (stem) * hs);
+             /*
+               Default position is on stem X, at stem end Y
+              */
+             o += Offset (0.5 *
+                          (n->extent (n,X_AXIS).length ()
+                           - stem->extent (stem,X_AXIS).length ())
+                           * (1 + Stem::get_direction (stem)),
+                           0);
            }
        }
-       else 
-         {
-           Real notewidth_f = extrema[d]->extent (X_AXIS).length ();
-           dy_f_drul_[d] = (int)rint (extrema[d]->head_positions_interval ()
-                                      [dir_]) * internote_f;
-           dx_f_drul_[d] += 0.5 * notewidth_f - d * gap_f;
+    }
+  else if (str == "loose-end")
+    {
+      SCM other_a = dir == LEFT ? gh_cdr (s) : gh_car (s);
+      if (ly_symbol2string (other_a) != "loose-end")
+       {
+#if 0
+         /*
+           The braindead way: horizontal
+         */
+         o = Offset (0, get_attachment (me, -dir, common)[Y_AXIS]);
+#else
+         o = broken_trend_offset (me, dir);
+#endif
+
+         
        }
-       dy_f_drul_[d] += dir_ * interline_f;
-       if (extrema[d]->stem_l_ && (dir_ == extrema[d]->stem_l_->dir_))
-         dy_f_drul_[d] -= dir_ * internote_f;
-      }
-  while (flip(&d) != LEFT);
+       
+    }
 
-  // now that both are set, do dependent
-  do 
+  SCM alist = me->get_elt_property ("extremity-offset-alist");
+int stemdir = stem ? Stem::get_direction (stem) : 1;
+  int slurdir = gh_scm2int (me->get_elt_property ("direction"));
+  SCM l = scm_assoc
+      (scm_listify (a,
+                gh_int2scm (stemdir * dir),
+                gh_int2scm (slurdir * dir),
+                  SCM_UNDEFINED), alist);
+
+  if (l != SCM_BOOL_F)
     {
-      if (broken_edge_b (this, extrema, d))
-        {
-         Direction u = d;
-         flip(&u);
+      o += ly_scm2offset (gh_cdr (l)) * staff_space * dir;
+    }
+
+  /*
+    What if get_bound () is not a note-column?
+   */
+  if (str != "loose-end"
+      && sp->get_bound (dir)->common_refpoint (common[Y_AXIS], Y_AXIS) == common[Y_AXIS])
+    {      
+      o[Y_AXIS] += sp->get_bound (dir)->relative_coordinate (common[Y_AXIS], Y_AXIS) 
+       - me->relative_coordinate (common[Y_AXIS], Y_AXIS);
+    }
 
-         // postbreak
-         if (d == LEFT)
-           dy_f_drul_[u] += dir_ * internote_f;
+  o += ly_scm2offset (index_cell (me->get_elt_property ("attachment-offset"),
+                                 dir)) * staff_space;
+  return o;
+}
 
-         dy_f_drul_[d] = dy_f_drul_[u];
-       }
-     }
-  while (flip(&d) != LEFT);
+Offset
+Slur::encompass_offset (Score_element*me,
+                       Score_element* col,
+                       Score_element **common) 
+{
+  Offset o;
+  Score_element* stem_l = unsmob_element (col->get_elt_property ("stem"));
+  
+  Direction dir = Directional_element_interface::get (me);
+  
+  if (!stem_l)
+    {
+      warning (_ ("Slur over rest?"));
+      o[X_AXIS] = col->relative_coordinate (common[X_AXIS], X_AXIS);
+      o[Y_AXIS] = col->relative_coordinate (common[Y_AXIS], Y_AXIS);
+      return o;  
+    }
+  Direction stem_dir = Directional_element_interface::get (stem_l);
+  o[X_AXIS] = stem_l->relative_coordinate (0, X_AXIS);
 
   /*
-    Slur should follow line of music
+    Simply set x to middle of notehead
    */
-  if (normal_edge_b (this, extrema, LEFT)
-      && normal_edge_b (this, extrema, RIGHT)
-      && (extrema[LEFT]->stem_l_ != extrema[RIGHT]->stem_l_))
+
+  o[X_AXIS] -= 0.5 * stem_dir * col->extent (col,X_AXIS).length ();
+
+  if ((stem_dir == dir)
+      && !stem_l->extent (stem_l, Y_AXIS).empty_b ())
     {
-      Real note_dy = extrema[RIGHT]->stem_l_->head_positions ()[dir_]
-       - extrema[LEFT]->stem_l_->head_positions ()[dir_];
-      Real dy = dy_f_drul_[RIGHT] - dy_f_drul_[LEFT];
-      /*
-       Should we always follow note-heads, (like a tie)?
-       For now, only if the note_dy != slur_dy, we'll do
-       slur_dy := note_dy * factor.
-      */
-      if (sign (dy) != sign (note_dy))
-       {
-         Real damp_f = paper ()->get_var ("slur_slope_follow_music_factor");
-         Real realdy = note_dy * damp_f;
-         Direction adjust_dir = (Direction)(- dir_ * sign (realdy));
-         if (!adjust_dir)
-           adjust_dir = -dir_;
-         /*
-           adjust only if no beam gets in the way
-          */
-         if (!extrema[adjust_dir]->stem_l_->beam_l_
-             || (adjust_dir == extrema[adjust_dir]->stem_l_->dir_)
-             || (extrema[adjust_dir]->stem_l_->beams_i_drul_[-adjust_dir] < 1))
-           {
-             dy_f_drul_[adjust_dir] = dy_f_drul_[-adjust_dir]
-               + 2 * adjust_dir * realdy;
-             Real dx = notewidth_f / 2;
-             if (adjust_dir != extrema[adjust_dir]->stem_l_->dir_)
-               dx /= 2;
-             dx_f_drul_[adjust_dir] -= adjust_dir * dx;
-           }
-       }
+      o[Y_AXIS] = stem_l->extent (common[Y_AXIS], Y_AXIS)[dir];
+    }
+  else
+    {
+      o[Y_AXIS] = col->extent (common[Y_AXIS], Y_AXIS)[dir];
     }
 
   /*
-    Avoid too steep slurs.
+   leave a gap: slur mustn't touch head/stem
    */
-  Real damp_f = paper ()->get_var ("slur_slope_damping");
-  Offset d_off = Offset (dx_f_drul_[RIGHT] - dx_f_drul_[LEFT],
-    dy_f_drul_[RIGHT] - dy_f_drul_[LEFT]);
-  d_off[X_AXIS] += extent (X_AXIS).length ();
-
-  Real ratio_f = abs (d_off[Y_AXIS] / d_off[X_AXIS]);
-  if (ratio_f > damp_f)
-    dy_f_drul_[(Direction)(- dir_ * sign (d_off[Y_AXIS]))] +=
-      dir_ * (ratio_f - damp_f) * d_off[X_AXIS];
+  o[Y_AXIS] += dir * gh_scm2double (me->get_elt_property ("y-free")) *
+    1.0;
+  return o;
 }
 
 Array<Offset>
-Slur::get_encompass_offset_arr () const
+Slur::get_encompass_offset_arr (Score_element *me)
 {
-  Real notewidth = paper ()->note_width () * 0.8;
-  Real gap = paper ()->get_var ("slur_x_gap");
-  Real internote = paper ()->internote_f ();
+  Spanner*sp = dynamic_cast<Spanner*>(me);
+  SCM eltlist = me->get_elt_property ("note-columns");
+  Score_element *common[] = {me->common_refpoint (eltlist, X_AXIS),
+                            me->common_refpoint (eltlist, Y_AXIS)};
+
+
+  common[X_AXIS] = common[X_AXIS]->common_refpoint (sp->get_bound (RIGHT), X_AXIS);
+  common[X_AXIS] = common[X_AXIS]->common_refpoint (sp->get_bound (LEFT), X_AXIS);
+  
+  Link_array<Score_element>  encompass_arr;
+  while (gh_pair_p (eltlist))
+    {
+      encompass_arr.push (unsmob_element (gh_car (eltlist)));      
+      eltlist =gh_cdr (eltlist);
+    }
+  encompass_arr.reverse ();
+
+  
+  Array<Offset> offset_arr;
+
+  Offset origin (me->relative_coordinate (common[X_AXIS], X_AXIS),
+                me->relative_coordinate (common[Y_AXIS], Y_AXIS)); 
+
+  int first = 1;
+  int last = encompass_arr.size () - 2;
 
-  Offset left = Offset (dx_f_drul_[LEFT], dy_f_drul_[LEFT]);
-  left[X_AXIS] += encompass_arr_[0]->stem_l_->hpos_f ();
+  offset_arr.push (get_attachment (me, LEFT, common));
 
   /*
-    <URG>
-    i don't understand these two, but *must* for symmetry 
-    look at encompass array: 
-       lilypond -D input/test/slur-symmetry*.ly
-       lilypond -D input/test/sleur.ly
+    left is broken edge
+  */
 
-    do_post_processing should have calculated these into
-    dx_f_drul_[], no??
+  if (encompass_arr[0] != sp->get_bound (LEFT))
+    {
+      first--;
 
-   */
+      // ?
+      offset_arr[0][Y_AXIS] -=
+       encompass_arr[0]->relative_coordinate (common[Y_AXIS], Y_AXIS) 
+       - me->relative_coordinate (common[Y_AXIS], Y_AXIS); 
+    }
 
-  if (dir_ != encompass_arr_[0]->stem_l_->dir_)
-    left[X_AXIS] += - 0.5 * notewidth * encompass_arr_[0]->stem_l_->dir_
-      + gap;
-  else if (encompass_arr_[0]->stem_l_->dir_ == UP)
-    left[X_AXIS] -= notewidth;
+  /*
+    right is broken edge
+  */
+  if (encompass_arr.top () != sp->get_bound (RIGHT))
+    {
+      last++;
+    }
 
-  if ((dir_ == encompass_arr_[0]->stem_l_->dir_) 
-    && (encompass_arr_[0]->stem_l_->dir_ == DOWN))
-    left[Y_AXIS] -= internote * encompass_arr_[0]->stem_l_->dir_;
-  /* </URG> */
+  for (int i = first; i <= last; i++)
+    {
+      Offset o (encompass_offset (me, encompass_arr[i], common));
+      offset_arr.push (o - origin);
+    }
 
-  Offset d = Offset (dx_f_drul_[RIGHT] - dx_f_drul_[LEFT],
-    dy_f_drul_[RIGHT] - dy_f_drul_[LEFT]);
-  d[X_AXIS] += extent (X_AXIS).length ();
+  offset_arr.push (Offset (sp->spanner_length (), 0) + get_attachment (me, RIGHT,common));
+
+  if (encompass_arr[0] != sp->get_bound (LEFT))
+    {
+      offset_arr.top ()[Y_AXIS] -= encompass_arr.top ()->relative_coordinate (common[Y_AXIS], Y_AXIS) 
+       - me->relative_coordinate (common[Y_AXIS], Y_AXIS);
+    }
+
+  return offset_arr;
+}
 
-  int first = 1;
-  int last = encompass_arr_.size () - 1;
 
-  // prebreak
-  if (encompass_arr_.top () != spanned_drul_[RIGHT])
-    last++;
+MAKE_SCHEME_CALLBACK(Slur,set_spacing_rods,1);
+SCM
+Slur::set_spacing_rods (SCM smob)
+{
+  Score_element*me = unsmob_element (smob);
 
-  // postbreak
-  if (encompass_arr_[0] != spanned_drul_[LEFT])
-    first--;
+  Rod r;
+  Spanner*sp = dynamic_cast<Spanner*>(me);
+  r.item_l_drul_[LEFT] = sp->get_bound (LEFT);
+  r.item_l_drul_[RIGHT] = sp->get_bound (RIGHT);
+  r.distance_f_ =
+    gh_scm2double (me->get_elt_property ("minimum-length"))
+    * 1.0;
+
+  r.add_to_cols ();
+  return SCM_UNSPECIFIED;
+}
 
-  Array<Offset> notes;
-  notes.push (Offset (0,0));
 
-  for (int i = first; i < last; i++)
+/*
+  Ugh should have dash-length + dash-period
+ */
+MAKE_SCHEME_CALLBACK (Slur, brew_molecule,1);
+SCM
+Slur::brew_molecule (SCM smob)
+{
+  Score_element * me = unsmob_element (smob);
+  if (!scm_ilength (me->get_elt_property ("note-columns")))
     {
-      Encompass_info info (encompass_arr_[i], dir_);
-      notes.push (info.o_ - left);
+      me->suicide ();
+      return SCM_EOL;
     }
-  Encompass_info info (encompass_arr_.top (), dir_);
-  // [encompass_arr_.size () - 1]
+
+  Real thick = me->paper_l ()->get_var ("stafflinethickness") *
+    gh_scm2double (me->get_elt_property ("thickness"));
+  Bezier one = get_curve (me);
+
+  // get_curve may suicide
+  if (!scm_ilength (me->get_elt_property ("note-columns")))
+    return SCM_EOL;
+
+  Molecule a;
+  SCM d =  me->get_elt_property ("dashed");
+  if (gh_number_p (d))
+    a = Lookup::dashed_slur (one, thick, thick * gh_scm2double (d));
+  else
+    a = Lookup::slur (one, Directional_element_interface::get (me) * thick, thick);
+
+  return a.create_scheme();
+}
+
+void
+Slur::set_control_points (Score_element*me)
+{
+  Real staff_space = Staff_symbol_referencer::staff_space ((Score_element*)me);
+
+  SCM details = me->get_elt_property ("details");
+  SCM h_inf_scm = scm_assq (ly_symbol2scm ("height-limit"), details);
+  SCM r_0_scm = scm_assq (ly_symbol2scm ("ratio"), details);
+
+  Real r_0 = gh_scm2double (gh_cdr (r_0_scm));
+  Real h_inf = staff_space * gh_scm2double (gh_cdr (h_inf_scm));
   
-  // urg:
-  Slur* urg = (Slur*)this;
-  urg->interstaff_f_ = info.interstaff_f_;
+  Slur_bezier_bow bb (get_encompass_offset_arr (me),
+                     Directional_element_interface::get (me),
+                     h_inf, r_0);
+
+  if (bb.fit_factor () > 1.0)
+    {
+      Real length = bb.curve_.control_[3][X_AXIS]; 
+      Real default_height = slur_height (length, h_inf, r_0);
+
+      SCM ssb = me->get_elt_property ("beautiful");
+      Real sb = 0;
+      if (gh_number_p (ssb))
+       sb = gh_scm2double (ssb);
+
+      bb.minimise_enclosed_area ( sb, details);
+      SCM sbf = scm_assq (ly_symbol2scm ("force-blowfit"), details);
+      Real bff = 1.0;
+      if (gh_pair_p (sbf) && gh_number_p (gh_cdr (sbf)))
+         bff = gh_scm2double (gh_cdr (sbf));
+
+      bb.curve_.control_[1][Y_AXIS] *= bff;
+      bb.curve_.control_[2][Y_AXIS] *= bff;
+      bb.blow_fit ();
+
+      
+      Real beautiful = length * default_height * sb;
+      Real area = bb.enclosed_area_f ();
+      
+      /*
+       Slurs that fit beautifully are not ugly
+      */
+      if (area > beautiful)
+       de_uglyfy (me, &bb, default_height);
+    }
+
+  Bezier b = bb.get_bezier ();
+
+
+  SCM controls = SCM_EOL;
+  for (int i= 4; i--;)
+    {
+      controls = gh_cons ( ly_offset2scm (b.control_[i]), controls);
+      /*
+       BRRR WHURG.
+       All these null control-points, where do they all come from?
+      */
+      if (i && b.control_[i][X_AXIS] == 0)
+       {
+         me->suicide ();
+         return;
+       }
+    }
+
+  me->set_elt_property ("control-points", controls);
+}
+  
+Bezier
+Slur::get_curve (Score_element*me) 
+{
+  Bezier b;
+  int i = 0;
+
+  if (!Directional_element_interface::get (me)
+      || ! gh_symbol_p (index_cell (me->get_elt_property ("attachment"), LEFT))
+      || ! gh_symbol_p (index_cell (me->get_elt_property ("attachment"), RIGHT)))
+    set_extremities (me);
   
-  d[Y_AXIS] += interstaff_f_;
+  if (!gh_pair_p (me->get_elt_property ("control-points")))
+    set_control_points (me);
+
+  // set_control_points may suicide
+  if (!scm_ilength (me->get_elt_property ("note-columns")))
+    return b;
 
-  // prebreak
-  if (interstaff_f_ && (encompass_arr_.top () != spanned_drul_[RIGHT]))
+  for (SCM s= me->get_elt_property ("control-points"); s != SCM_EOL; s = gh_cdr (s))
     {
-      Encompass_info info (encompass_arr_[encompass_arr_.size () - 1], dir_);
-      d[Y_AXIS] -= info.o_[Y_AXIS] - interstaff_f_;
+      b.control_[i] = ly_scm2offset (gh_car (s));
+      i++;
     }
 
-  notes.push (d);
+  Array<Offset> enc (get_encompass_offset_arr (me));
+  Direction dir = Directional_element_interface::get (me);
+  
+  Real x1 = enc[0][X_AXIS];
+  Real x2 = enc.top ()[X_AXIS];
 
-  return notes;
+  Real off = 0.0;
+  for (int i=1; i < enc.size ()-1; i++)
+    {
+      Real x = enc[i][X_AXIS];
+      if (x > x1 && x <x2)
+       {
+         Real y = b.get_other_coordinate (X_AXIS, x);
+         off = off >? dir *  (enc[i][Y_AXIS] - y);
+       }
+    }
+  b.translate (Offset (0, dir * off));
+  return b;
 }
 
 
-Array<Rod>
-Slur::get_rods () const
+bool
+Slur::has_interface (Score_element*me)
 {
-  Array<Rod> a;
-  Rod r;
-  r.item_l_drul_ = spanned_drul_;
-  r.distance_f_ = paper ()->get_var ("slur_x_minimum");
-
-  a.push (r);
-  return a;
+  return me->has_interface (ly_symbol2scm ("slur-interface"));
 }
 
+