]> git.donarmstrong.com Git - lilypond.git/blobdiff - lily/stem.cc
(conv): \apply -> \applymusic
[lilypond.git] / lily / stem.cc
index 502ca9dc5cbdf527edff070570fe4874c3f62a75..cd36896bc3f7727f3a9fdf93ae0b183f98061c93 100644 (file)
@@ -3,7 +3,7 @@
 
   source file of the GNU LilyPond music typesetter
 
 
   source file of the GNU LilyPond music typesetter
 
-  (c) 1996--2002 Han-Wen Nienhuys <hanwen@cs.uu.nl>
+  (c) 1996--2004 Han-Wen Nienhuys <hanwen@cs.uu.nl>
     Jan Nieuwenhuizen <janneke@gnu.org>
 
   TODO: This is way too hairy
     Jan Nieuwenhuizen <janneke@gnu.org>
 
   TODO: This is way too hairy
@@ -23,7 +23,7 @@
 #include "paper-def.hh"
 #include "rhythmic-head.hh"
 #include "font-interface.hh"
 #include "paper-def.hh"
 #include "rhythmic-head.hh"
 #include "font-interface.hh"
-#include "molecule.hh"
+#include "stencil.hh"
 #include "paper-column.hh"
 #include "misc.hh"
 #include "beam.hh"
 #include "paper-column.hh"
 #include "misc.hh"
 #include "beam.hh"
 #include "spanner.hh"
 #include "side-position-interface.hh"
 #include "dot-column.hh"
 #include "spanner.hh"
 #include "side-position-interface.hh"
 #include "dot-column.hh"
+#include "stem-tremolo.hh"
 
 void
 Stem::set_beaming (Grob*me, int beam_count,  Direction d)
 {
 
 void
 Stem::set_beaming (Grob*me, int beam_count,  Direction d)
 {
-  SCM pair = me->get_grob_property ("beaming");
-  
-  if (!gh_pair_p (pair))
+  SCM pair = me->get_property ("beaming");
+
+  if (!ly_c_pair_p (pair))
     {
     {
-      pair = gh_cons (SCM_EOL, SCM_EOL);
-      me->set_grob_property ("beaming", pair);
+      pair = scm_cons (SCM_EOL, SCM_EOL);
+      me->set_property ("beaming", pair);
     }
 
   SCM l = index_get_cell (pair, d);
     }
 
   SCM l = index_get_cell (pair, d);
-  forint i = 0; i<  beam_count; i++)
+  for (int i = 0; i<  beam_count; i++)
     {
     {
-      l = gh_cons (gh_int2scm (i), l);
+      l = scm_cons (scm_int2num (i), l);
     }
   index_set_cell (pair, d, l);         
 }
 
 
 Interval
     }
   index_set_cell (pair, d, l);         
 }
 
 
 Interval
-Stem::head_positions (Grob*me) 
+Stem::head_positions (Grob*me)
 {
   if (!head_count (me))
     {
 {
   if (!head_count (me))
     {
@@ -65,96 +66,84 @@ Stem::head_positions (Grob*me)
 
   Drul_array<Grob*> e (extremal_heads (me));
 
 
   Drul_array<Grob*> e (extremal_heads (me));
 
-  return Interval (Staff_symbol_referencer::position_f (e[DOWN]),
-                  Staff_symbol_referencer::position_f (e[UP]));
+  return Interval (Staff_symbol_referencer::get_position (e[DOWN]),
+                  Staff_symbol_referencer::get_position (e[UP]));
 }
 
 
 Real
 }
 
 
 Real
-Stem::chord_start_y (Grob*me) 
+Stem::chord_start_y (Grob *me)
 {
 {
-  return head_positions (me)[get_direction (me)]
-    * Staff_symbol_referencer::staff_space (me)/2.0;
+  Interval hp = head_positions (me);
+  if (hp.is_empty ())
+    return 0;
+  return hp[get_direction (me)] * Staff_symbol_referencer::staff_space (me)
+    * 0.5;
 }
 
 Real
 }
 
 Real
-Stem::stem_end_position (Grob*me) 
+Stem::stem_end_position (Grob *me)
 {
 {
-  SCM p =me->get_grob_property ("stem-end-position");
+  SCM p = me->get_property ("stem-end-position");
   Real pos;
   Real pos;
-  if (!gh_number_p (p))
+  if (!ly_c_number_p (p))
     {
       pos = get_default_stem_end_position (me);
     {
       pos = get_default_stem_end_position (me);
-      me->set_grob_property ("stem-end-position", gh_double2scm (pos));
+      me->set_property ("stem-end-position", scm_make_real (pos));
     }
   else
     }
   else
-    pos = gh_scm2double (p);
+    pos = ly_scm2double (p);
 
   return pos;
 }
 
 Direction
 
   return pos;
 }
 
 Direction
-Stem::get_direction (Grob*me)
+Stem::get_direction (Grob *me)
 {
 {
-  Direction d = Directional_element_interface::get (me);
+  Direction d = get_grob_direction (me);
 
   if (!d)
     {
        d = get_default_dir (me);
        // urg, AAARGH!
 
   if (!d)
     {
        d = get_default_dir (me);
        // urg, AAARGH!
-       Directional_element_interface::set (me, d);
+       set_grob_direction (me, d);
     }
     }
-  return d ;
+  return d;
 }
 
 }
 
-
 void
 void
-Stem::set_stemend (Grob*me, Real se)
+Stem::set_stemend (Grob *me, Real se)
 {
   // todo: margins
   Direction d= get_direction (me);
 {
   // todo: margins
   Direction d= get_direction (me);
-  
+
   if (d && d * head_positions (me)[get_direction (me)] >= se*d)
     me->warning (_ ("Weird stem size; check for narrow beams"));
 
   if (d && d * head_positions (me)[get_direction (me)] >= se*d)
     me->warning (_ ("Weird stem size; check for narrow beams"));
 
-  me->set_grob_property ("stem-end-position", gh_double2scm (se));
+  me->set_property ("stem-end-position", scm_make_real (se));
 }
 
 }
 
-
-/*
-  Note head that determines hshift for upstems
- */ 
+/* Note head that determines hshift for upstems
+   WARNING: triggers direction  */
 Grob*
 Grob*
-Stem::support_head (Grob*me)
+Stem::support_head (Grob *me)
 {
 {
-  SCM h = me->get_grob_property ("support-head");
-  Grob * nh = unsmob_grob (h);
-  if (nh)
-    return nh;
-  else if (head_count (me) == 1)
-    {
-      /*
-       UGH.
-       */
-      
-      return unsmob_grob (ly_car (me->get_grob_property ("note-heads")));
-    }
-  else
-    return first_head (me);
+  if (head_count (me) == 1)
+    /* UGH. */
+    return unsmob_grob (ly_car (me->get_property ("note-heads")));
+  return first_head (me);
 }
 
 }
 
-
 int
 Stem::head_count (Grob*me)
 {
   return  Pointer_group_interface::count (me, "note-heads");
 }
 
 int
 Stem::head_count (Grob*me)
 {
   return  Pointer_group_interface::count (me, "note-heads");
 }
 
-/*
-  The note head which forms one end of the stem.  
- */
+/* The note head which forms one end of the stem.
+   WARNING: triggers direction  */
 Grob*
 Grob*
-Stem::first_head (Grob*me)
+Stem::first_head (Grob *me)
 {
   Direction d = get_direction (me);
   if (!d)
 {
   Direction d = get_direction (me);
   if (!d)
@@ -162,11 +151,9 @@ Stem::first_head (Grob*me)
   return extremal_heads (me)[-d];
 }
 
   return extremal_heads (me)[-d];
 }
 
-/*
-  The note head opposite to the first head.
- */
+/* The note head opposite to the first head.  */
 Grob*
 Grob*
-Stem::last_head (Grob*me)
+Stem::last_head (Grob *me)
 {
   Direction d = get_direction (me);
   if (!d)
 {
   Direction d = get_direction (me);
   if (!d)
@@ -174,191 +161,192 @@ Stem::last_head (Grob*me)
   return extremal_heads (me)[d];
 }
 
   return extremal_heads (me)[d];
 }
 
-/*
-  START is part where stem reaches `last' head. 
- */
+/* START is part where stem reaches `last' head.  */
 Drul_array<Grob*>
 Drul_array<Grob*>
-Stem::extremal_heads (Grob*me) 
+Stem::extremal_heads (Grob *me)
 {
   const int inf = 1000000;
   Drul_array<int> extpos;
   extpos[DOWN] = inf;
 {
   const int inf = 1000000;
   Drul_array<int> extpos;
   extpos[DOWN] = inf;
-  extpos[UP] = -inf;  
-  
+  extpos[UP] = -inf;
+
   Drul_array<Grob *> exthead;
   exthead[LEFT] = exthead[RIGHT] =0;
   Drul_array<Grob *> exthead;
   exthead[LEFT] = exthead[RIGHT] =0;
-  
-  for (SCM s = me->get_grob_property ("note-heads"); gh_pair_p (s); s = ly_cdr (s))
-    {
-      Grob * n = unsmob_grob (ly_car (s));
 
 
-      
-      int p = int (Staff_symbol_referencer::position_f (n));
+  for (SCM s = me->get_property ("note-heads"); ly_c_pair_p (s); s = ly_cdr (s))
+    {
+      Grob *n = unsmob_grob (ly_car (s));
+      int p = Staff_symbol_referencer::get_rounded_position (n);
 
       Direction d = LEFT;
 
       Direction d = LEFT;
-      do {
-      if (d* p > d* extpos[d])
+      do
        {
        {
-         exthead[d] = n;
-         extpos[d] = p;
-       }
-      } while (flip (&d) != DOWN);
+         if (d* p > d* extpos[d])
+           {
+             exthead[d] = n;
+             extpos[d] = p;
+           }
+       } while (flip (&d) != DOWN);
     }
     }
-
   return exthead;
 }
 
 static int
 icmp (int const &a, int const &b)
 {
   return exthead;
 }
 
 static int
 icmp (int const &a, int const &b)
 {
-  return a-b;
+  return a - b;
 }
 
 }
 
+/* The positions, in ascending order.  */
 Array<int>
 Stem::note_head_positions (Grob *me)
 {
   Array<int> ps ;
 Array<int>
 Stem::note_head_positions (Grob *me)
 {
   Array<int> ps ;
-  for (SCM s = me->get_grob_property ("note-heads"); gh_pair_p (s); s = ly_cdr (s))
+  for (SCM s = me->get_property ("note-heads"); ly_c_pair_p (s); s = ly_cdr (s))
     {
       Grob * n = unsmob_grob (ly_car (s));
     {
       Grob * n = unsmob_grob (ly_car (s));
-      int p = int (Staff_symbol_referencer::position_f (n));
+      int p = Staff_symbol_referencer::get_rounded_position (n);
 
       ps.push (p);
     }
 
   ps.sort (icmp);
 
       ps.push (p);
     }
 
   ps.sort (icmp);
-  return ps; 
+  return ps;
 }
 
 }
 
-
 void
 void
-Stem::add_head (Grob*me, Grob *n)
+Stem::add_head (Grob *me, Grob *n)
 {
 {
-  n->set_grob_property ("stem", me->self_scm ());
+  n->set_property ("stem", me->self_scm ());
   n->add_dependency (me);
 
   n->add_dependency (me);
 
+  /* TODO: why not store Rest pointers? */
   if (Note_head::has_interface (n))
   if (Note_head::has_interface (n))
-    {
-      Pointer_group_interface::add_grob (me, ly_symbol2scm ("note-heads"), n);
-    }
+    Pointer_group_interface::add_grob (me, ly_symbol2scm ("note-heads"), n);
 }
 
 bool
 }
 
 bool
-Stem::invisible_b (Grob*me)
+Stem::is_invisible (Grob *me)
 {
 {
-  return ! (head_count (me) && Note_head::balltype_i (support_head (me)) >= 1);
+  return !(head_count (me)
+          && ly_scm2int (me->get_property ("duration-log")) >= 1);
 }
 
 Direction
 }
 
 Direction
-Stem::get_default_dir (Grob*me) 
+Stem::get_default_dir (Grob *me)
 {
   int staff_center = 0;
   Interval hp = head_positions (me);
 {
   int staff_center = 0;
   Interval hp = head_positions (me);
-  if (hp.empty_b())
-    {
-      return CENTER;
-    }
-  
+  if (hp.is_empty ())
+    return CENTER;
+
   int udistance = (int) (UP * hp[UP] - staff_center);
   int udistance = (int) (UP * hp[UP] - staff_center);
-  int ddistance = (int) (DOWN* hp[DOWN] - staff_center);  
-  
+  int ddistance = (int) (DOWN * hp[DOWN] - staff_center);
+
   if (sign (ddistance - udistance))
   if (sign (ddistance - udistance))
-    return Direction (sign (ddistance -udistance));
+    return Direction (sign (ddistance - udistance));
 
 
-  return to_dir (me->get_grob_property ("neutral-direction"));
+  return to_dir (me->get_property ("neutral-direction"));
 }
 
 Real
 }
 
 Real
-Stem::get_default_stem_end_position (Grob*me) 
+Stem::get_default_stem_end_position (Grob*me)
 {
 {
-  SCM up_to_staff = me->get_grob_property ("up-to-staff");
-  if (to_boolean(up_to_staff))
-    {
-      int line_count = Staff_symbol_referencer::line_count (me);
-    
-      Direction dir = get_direction (me);
-    
-      return dir*  (line_count + 3.5);
-    }
-  
-  bool grace_b = to_boolean (me->get_grob_property ("grace"));
+  Real ss = Staff_symbol_referencer::staff_space (me);
+  int durlog = duration_log (me);
   SCM s;
   Array<Real> a;
 
   SCM s;
   Array<Real> a;
 
-  Real length_f = 3.5;
-  SCM scm_len = me->get_grob_property ("length");
-  if (gh_number_p (scm_len))
-    {
-      length_f = gh_scm2double (scm_len);
-    }
+  /* WARNING: IN HALF SPACES */
+  Real length = 7;
+  SCM scm_len = me->get_property ("length");
+  if (ly_c_number_p (scm_len))
+    length = ly_scm2double (scm_len);
   else
     {
   else
     {
-      s = me->get_grob_property ("lengths");
-      if (gh_pair_p (s))
-       {
-         length_f = 2* gh_scm2double (robust_list_ref (duration_log(me) -2, s));
-       }
+      s = me->get_property ("lengths");
+      if (ly_c_pair_p (s))
+       length = 2* ly_scm2double (robust_list_ref (durlog -2, s));
     }
 
     }
 
-
-  Real shorten_f = 0.0;
-  
-  SCM sshorten = me->get_grob_property ("stem-shorten");
-  if (gh_pair_p (sshorten))
-    {
-      shorten_f = 2* gh_scm2double (robust_list_ref ((duration_log (me) - 2) >? 0, sshorten));
-    }
-
-  /* On boundary: shorten only half */
-  if (abs (chord_start_y (me)) == 0.5)
-    shorten_f *= 0.5;
-
   /* URGURGURG
   /* URGURGURG
-     'set-default-stemlen' sets direction too
-   */
+     'set-default-stemlen' sets direction too.   */
   Direction dir = get_direction (me);
   if (!dir)
     {
       dir = get_default_dir (me);
   Direction dir = get_direction (me);
   if (!dir)
     {
       dir = get_default_dir (me);
-      Directional_element_interface::set (me, dir);
+      set_grob_direction (me, dir);
+    }
+
+  /* Stems in unnatural (forced) direction should be shortened,
+     according to [Roush & Gourlay] */
+  Interval hp = head_positions (me);
+  if (dir && dir * hp[dir] >= 0)
+    {
+      SCM sshorten = me->get_property ("stem-shorten");
+      SCM scm_shorten = ly_c_pair_p (sshorten) ?
+       robust_list_ref ((duration_log (me) - 2) >? 0, sshorten): SCM_EOL;
+      Real shorten = 2* robust_scm2double (scm_shorten,0);
+
+      /* On boundary: shorten only half */
+      if (abs (head_positions (me)[dir]) <= 1)
+       shorten *= 0.5;
+
+      length -= shorten;
+    }
+
+  /* Tremolo stuff.  */
+  Grob *t_flag = unsmob_grob (me->get_property ("tremolo-flag"));
+  if (t_flag && !unsmob_grob (me->get_property ("beam")))
+    {
+      /* Crude hack: add extra space if tremolo flag is there.
+
+       We can't do this for the beam, since we get into a loop
+       (Stem_tremolo::raw_stencil () looks at the beam.) --hwn  */
+
+      Real minlen = 1.0
+       + 2 * Stem_tremolo::raw_stencil (t_flag).extent (Y_AXIS).length  ()
+       / ss;
+
+      if (durlog >= 3)
+       {
+         Interval flag_ext = flag (me).extent (Y_AXIS) ;
+         if (!flag_ext.is_empty ())
+           minlen += 2 * flag_ext.length () / ss ;
+
+         /* The clash is smaller for down stems (since the tremolo is
+            angled up.) */
+         if (dir == DOWN)
+           minlen -= 1.0;
+       }
+      length = length >? (minlen + 1.0);
     }
     }
-  
-  /* stems in unnatural (forced) direction should be shortened, 
-    according to [Roush & Gourlay] */
-  if (chord_start_y (me)
-      && (get_direction (me) != get_default_dir (me)))
-    length_f -= shorten_f;
-
-  Interval hp = head_positions (me);  
-  Real st = hp[dir] + dir * length_f;
-
-  bool no_extend_b = to_boolean (me->get_grob_property ("no-stem-extend"));
-  if (!grace_b && !no_extend_b && dir * st < 0) // junkme?
+
+  Real st = dir ? hp[dir] + dir * length : 0;
+
+  /* TODO: change name  to extend-stems to staff/center/'()  */
+  bool no_extend_b = to_boolean (me->get_property ("no-stem-extend"));
+  if (!no_extend_b && dir * st < 0)
     st = 0.0;
 
     st = 0.0;
 
-  /*
-    Make a little room if we have a upflag and there is a dot.
-    previous approach was to lengthen the stem. This is not
-    good typesetting practice. 
-    
-  */
-  if (!beam_l (me) && dir == UP
-      && duration_log (me) > 2)
+  /* Make a little room if we have a upflag and there is a dot.
+     previous approach was to lengthen the stem. This is not
+     good typesetting practice.  */
+  if (!get_beam (me) && dir == UP
+      && durlog > 2)
     {
       Grob * closest_to_flag = extremal_heads (me)[dir];
       Grob * dots = closest_to_flag
     {
       Grob * closest_to_flag = extremal_heads (me)[dir];
       Grob * dots = closest_to_flag
-       ? Rhythmic_head::dots_l (closest_to_flag ) : 0;
+       ? Rhythmic_head::get_dots (closest_to_flag ) : 0;
 
       if (dots)
        {
 
       if (dots)
        {
-         Real dp = Staff_symbol_referencer::position_f  (dots);
+         Real dp = Staff_symbol_referencer::get_position (dots);
          Real flagy =  flag (me).extent (Y_AXIS)[-dir] * 2
          Real flagy =  flag (me).extent (Y_AXIS)[-dir] * 2
-           / Staff_symbol_referencer::staff_space (me); 
+           / ss;
 
 
-         /*
-           Very gory: add myself to the X-support of the parent,
-           which should be a dot-column.
-          */
+         /* Very gory: add myself to the X-support of the parent,
+            which should be a dot-column. */
          if (dir * (st + flagy -  dp) < 0.5)
            {
              Grob *par = dots->get_parent (X_AXIS);
          if (dir * (st + flagy -  dp) < 0.5)
            {
              Grob *par = dots->get_parent (X_AXIS);
@@ -367,31 +355,27 @@ Stem::get_default_stem_end_position (Grob*me)
                {
                  Side_position_interface::add_support (par, me);
 
                {
                  Side_position_interface::add_support (par, me);
 
-                 /*
-                   TODO: apply some better logic here. The flag is
-                   curved inwards, so this will typically be too
-                   much.
-                 */
+                 /* TODO: apply some better logic here. The flag is
+                    curved inwards, so this will typically be too
+                    much. */
                }
            }
        }
     }
                }
            }
        }
     }
-
-
   return st;
 }
 
 
 
 /*
   return st;
 }
 
 
 
 /*
-  
+
   the log of the duration (Number of hooks on the flag minus two)
  */
 int
   the log of the duration (Number of hooks on the flag minus two)
  */
 int
-Stem::duration_log (Grob*me) 
+Stem::duration_log (Grob*me)
 {
 {
-  SCM s = me->get_grob_property ("duration-log");
-  return (gh_number_p (s)) ? gh_scm2int (s) : 2;
+  SCM s = me->get_property ("duration-log");
+  return (ly_c_number_p (s)) ? ly_scm2int (s) : 2;
 }
 
 void
 }
 
 void
@@ -399,23 +383,18 @@ Stem::position_noteheads (Grob*me)
 {
   if (!head_count (me))
     return;
 {
   if (!head_count (me))
     return;
-  
+
   Link_array<Grob> heads =
     Pointer_group_interface__extract_grobs (me, (Grob*)0, "note-heads");
 
   heads.sort (compare_position);
   Direction dir =get_direction (me);
   Link_array<Grob> heads =
     Pointer_group_interface__extract_grobs (me, (Grob*)0, "note-heads");
 
   heads.sort (compare_position);
   Direction dir =get_direction (me);
-  
+
   if (dir < 0)
     heads.reverse ();
 
 
   if (dir < 0)
     heads.reverse ();
 
 
-  bool invisible = invisible_b (me);
-  Real thick = 0.0;
-  if (invisible)
-        thick = gh_scm2double (me->get_grob_property ("thickness"))
-         * me->paper_l ()->get_var ("linethickness");
-      
+  Real thick = thickness (me);
 
   Grob *hed = support_head (me);
   Real w = Note_head::head_extent (hed,X_AXIS)[dir];
 
   Grob *hed = support_head (me);
   Real w = Note_head::head_extent (hed,X_AXIS)[dir];
@@ -424,46 +403,61 @@ Stem::position_noteheads (Grob*me)
       heads[i]->translate_axis (w - Note_head::head_extent (heads[i],X_AXIS)[dir],
                                X_AXIS);
     }
       heads[i]->translate_axis (w - Note_head::head_extent (heads[i],X_AXIS)[dir],
                                X_AXIS);
     }
-  
-  bool parity= true;           // todo: make me settable.
-  int lastpos = int (Staff_symbol_referencer::position_f (heads[0]));
+
+  bool parity= true;
+  Real lastpos = Real (Staff_symbol_referencer::get_position (heads[0]));
   for (int i=1; i < heads.size (); i ++)
     {
   for (int i=1; i < heads.size (); i ++)
     {
-      Real p = Staff_symbol_referencer::position_f (heads[i]);
-      int dy =abs (lastpos- (int)p);
+      Real p = Staff_symbol_referencer::get_position (heads[i]);
+      Real dy =fabs (lastpos- p);
 
 
-      if (dy <= 1)
+      /*
+       dy should always be 0.5, 0.0, 1.0, but provide safety margin
+       for rounding errors.
+      */
+      if (dy < 1.1)            
        {
          if (parity)
            {
              Real l = Note_head::head_extent (heads[i], X_AXIS).length ();
 
              Direction d = get_direction (me);
        {
          if (parity)
            {
              Real l = Note_head::head_extent (heads[i], X_AXIS).length ();
 
              Direction d = get_direction (me);
-             heads[i]->translate_axis (l * d, X_AXIS);
+             /*
+               Reversed head should be shifted l-thickness, but this
+               looks too crowded, so we only shift l-0.5*thickness.
 
 
-             if (invisible_b(me))
-               heads[i]->translate_axis (-thick *2* d , X_AXIS);
+               This leads to assymetry: Normal heads overlap the
+               stem 100% whereas reversed heads only overlaps the
+               stem 50%
 
 
-             
+             */
+
+             Real reverse_overlap =0.5;
+             heads[i]->translate_axis ((l-thick*reverse_overlap) * d, X_AXIS);
+
+             if (is_invisible (me))
+               heads[i]->translate_axis (-thick*(2 - reverse_overlap) * d , X_AXIS);
+
+       
             /* TODO:
             /* TODO:
-                
+               
              For some cases we should kern some more: when the
              For some cases we should kern some more: when the
-             distance between the next or prev note is too large, we'd 
+             distance between the next or prev note is too large, we'd
              get large white gaps, eg.
              get large white gaps, eg.
-             
+       
                |
               X|
               |X  <- kern this.
               |
              X
                |
               X|
               |X  <- kern this.
               |
              X
-             
+       
              */
            }
          parity = !parity;
        }
       else
        parity = true;
              */
            }
          parity = !parity;
        }
       else
        parity = true;
-      
+
       lastpos = int (p);
     }
 }
       lastpos = int (p);
     }
 }
@@ -486,9 +480,9 @@ Stem::before_line_breaking (SCM smob)
     }
   else
     {
     }
   else
     {
-      me->remove_grob_property ("molecule-callback");
+      me->set_property ("print-function", SCM_EOL);
     }
     }
-  
+
   return SCM_UNSPECIFIED;
 }
 
   return SCM_UNSPECIFIED;
 }
 
@@ -501,44 +495,47 @@ MAKE_SCHEME_CALLBACK (Stem, height, 2);
 SCM
 Stem::height (SCM smob, SCM ax)
 {
 SCM
 Stem::height (SCM smob, SCM ax)
 {
-  Axis a = (Axis)gh_scm2int (ax);
+  Axis a = (Axis)ly_scm2int (ax);
   Grob * me = unsmob_grob (smob);
   assert (a == Y_AXIS);
 
   Grob * me = unsmob_grob (smob);
   assert (a == Y_AXIS);
 
-  SCM mol = me->get_uncached_molecule ();
+  SCM mol = me->get_uncached_stencil ();
   Interval iv;
   if (mol != SCM_EOL)
   Interval iv;
   if (mol != SCM_EOL)
-    iv = unsmob_molecule (mol)->extent (a);
+    iv = unsmob_stencil (mol)->extent (a);
+  if (Grob *b =get_beam (me))
+    {
+      Direction d = get_direction (me);
+      iv[d] += d * Beam::get_thickness (b) * 0.5 ;
+    }
+
   return ly_interval2scm (iv);
 }
 
 
   return ly_interval2scm (iv);
 }
 
 
-Molecule
+Stencil
 Stem::flag (Grob*me)
 {
 Stem::flag (Grob*me)
 {
-  /* TODO: rename flag-style into something more appropriate,
-   e.g. "stroke-style", maybe with values "" (i.e. no stroke),
-   "single" and "double".  Needs more discussion.
-  */
-  String style, fstyle, staffline_offs;
-  SCM fst = me->get_grob_property ("flag-style");
-  if (gh_string_p (fst))
-    {
-      fstyle = ly_scm2string (fst);
-    }
+  /* TODO: maybe property stroke-style should take different values,
+     e.g. "" (i.e. no stroke), "single" and "double" (currently, it's
+     '() or "grace").  */
+  String flag_style;
 
 
-  SCM st = me->get_grob_property ("style");
-  if (gh_symbol_p (st))
+  SCM flag_style_scm = me->get_property ("flag-style");
+  if (ly_c_symbol_p (flag_style_scm))
     {
     {
-      style = (ly_scm2string (scm_symbol_to_string (st)));
+      flag_style = ly_symbol2string (flag_style_scm);
     }
     }
-  else
+
+  if (flag_style == "no-flag")
     {
     {
-      style = "";
+      return Stencil ();
     }
     }
-  bool adjust = to_boolean (me->get_grob_property ("adjust-if-on-staffline"));
 
 
-  if (String::compare_i (style, "mensural") == 0)
+  bool adjust = true;
+
+  String staffline_offs;
+  if (String::compare (flag_style, "mensural") == 0)
     /* Mensural notation: For notes on staff lines, use different
        flags than for notes between staff lines.  The idea is that
        flags are always vertically aligned with the staff lines,
     /* Mensural notation: For notes on staff lines, use different
        flags than for notes between staff lines.  The idea is that
        flags are always vertically aligned with the staff lines,
@@ -551,7 +548,7 @@ Stem::flag (Grob*me)
         {
          /* Urrgh!  We have to detect wether this stem ends on a staff
             line or between two staff lines.  But we can not call
         {
          /* Urrgh!  We have to detect wether this stem ends on a staff
             line or between two staff lines.  But we can not call
-            stem_end_position(me) or get_default_stem_end_position(me),
+            stem_end_position (me) or get_default_stem_end_position (me),
             since this encounters the flag and hence results in an
             infinite recursion.  However, in pure mensural notation,
             there are no multiple note heads attached to a single stem,
             since this encounters the flag and hence results in an
             infinite recursion.  However, in pure mensural notation,
             there are no multiple note heads attached to a single stem,
@@ -564,18 +561,18 @@ Stem::flag (Grob*me)
             flag's shape accordingly.  In the worst case, the shape
             looks slightly misplaced, but that will usually be the
             programmer's fault (e.g. when trying to attach multiple
             flag's shape accordingly.  In the worst case, the shape
             looks slightly misplaced, but that will usually be the
             programmer's fault (e.g. when trying to attach multiple
-            note heads to a single stem in mensural notation).  */
+            note heads to a single stem in mensural notation).
+         */
 
          /*
            perhaps the detection whether this correction is needed should
            happen in a different place  to avoid the recursion.
 
          /*
            perhaps the detection whether this correction is needed should
            happen in a different place  to avoid the recursion.
-           
+       
            --hwn.
          */
            --hwn.
          */
-         Grob *first = first_head(me);
-         int sz = Staff_symbol_referencer::line_count (me)-1;
-         int p = (int)rint (Staff_symbol_referencer::position_f (first));
-         staffline_offs = (((p ^ sz) & 0x1) == 0) ? "1" : "0";
+         int p = Staff_symbol_referencer::get_rounded_position (me);
+         staffline_offs = Staff_symbol_referencer::on_staffline (me, p) ?
+           "1" : "0";
        }
       else
         {
        }
       else
         {
@@ -586,76 +583,90 @@ Stem::flag (Grob*me)
     {
       staffline_offs = "";
     }
     {
       staffline_offs = "";
     }
-  char c = (get_direction (me) == UP) ? 'u' : 'd';
-  String index_str
-    = String ("flags-") + style + to_str (c) + staffline_offs + to_str (duration_log (me));
-  Molecule m
-    = Font_interface::get_default_font (me)->find_by_name (index_str);
-  if (!fstyle.empty_b ())
-    m.add_molecule (Font_interface::get_default_font (me)->find_by_name (String ("flags-") + to_str (c) + fstyle));
-  return m;
+
+  char dir = (get_direction (me) == UP) ? 'u' : 'd';
+  String font_char =
+    flag_style + to_string (dir) + staffline_offs + to_string (duration_log (me));
+  Font_metric *fm = Font_interface::get_default_font (me);
+  Stencil flag = fm->find_by_name ("flags-" + font_char);
+  if (flag.is_empty ())
+    {
+      me->warning (_f ("flag `%s' not found", font_char));
+    }
+
+  SCM stroke_style_scm = me->get_property ("stroke-style");
+  if (ly_c_string_p (stroke_style_scm))
+    {
+      String stroke_style = ly_scm2string (stroke_style_scm);
+      if (!stroke_style.is_empty ())
+       {
+         String font_char = to_string (dir) + stroke_style;
+         Stencil stroke = fm->find_by_name ("flags-" + font_char);
+         if (stroke.is_empty ())
+           {
+             me->warning (_f ("flag stroke `%s' not found", font_char));
+           }
+         else
+           {
+             flag.add_stencil (stroke);
+           }
+       }
+    }
+
+  return flag;
 }
 
 MAKE_SCHEME_CALLBACK (Stem,dim_callback,2);
 SCM
 Stem::dim_callback (SCM e, SCM ax)
 {
 }
 
 MAKE_SCHEME_CALLBACK (Stem,dim_callback,2);
 SCM
 Stem::dim_callback (SCM e, SCM ax)
 {
-  Axis a = (Axis) gh_scm2int (ax);
+  Axis a = (Axis) ly_scm2int (ax);
   assert (a == X_AXIS);
   assert (a == X_AXIS);
-  Grob *se = unsmob_grob (e);
+  Grob *me = unsmob_grob (e);
   Interval r (0, 0);
   Interval r (0, 0);
-  if (unsmob_grob (se->get_grob_property ("beam")) || abs (duration_log (se)) <= 2)
+  if (unsmob_grob (me->get_property ("beam")) || abs (duration_log (me)) <= 2)
     ;  // TODO!
   else
     {
     ;  // TODO!
   else
     {
-      r = flag (se).extent (X_AXIS);
+      r = flag (me).extent (X_AXIS)
+       + thickness (me)/2;
     }
   return ly_interval2scm (r);
 }
     }
   return ly_interval2scm (r);
 }
 
 
+Real
+Stem::thickness (Grob* me)
+{
+  return ly_scm2double (me->get_property ("thickness"))
+    * Staff_symbol_referencer::line_thickness (me);
+}
 
 
-MAKE_SCHEME_CALLBACK (Stem,brew_molecule,1);
+MAKE_SCHEME_CALLBACK (Stem,print,1);
 
 SCM
 
 SCM
-Stem::brew_molecule (SCM smob) 
+Stem::print (SCM smob)
 {
   Grob*me = unsmob_grob (smob);
 {
   Grob*me = unsmob_grob (smob);
-  Molecule mol;
+  Stencil mol;
   Direction d = get_direction (me);
   Direction d = get_direction (me);
-  
-  
-     
-  Real y1;
 
   /*
 
   /*
+    TODO: make the stem start a direction ?
+
     This is required to avoid stems passing in tablature chords...
     This is required to avoid stems passing in tablature chords...
-   */
+  */
+  Grob *lh = to_boolean (me->get_property ("avoid-note-head"))
+    ? last_head (me) :  lh = first_head (me);
 
 
+  if (!lh)
+    return SCM_EOL;
 
 
-  /*
-    TODO: make  the stem start a direction ?
-  */
-  
+  if (is_invisible (me))
+    return SCM_EOL;
 
 
-  
-  if (to_boolean (me->get_grob_property ("avoid-note-head")))
-    {
-      Grob * lh = last_head (me);
-      if (!lh)
-       return SCM_EOL;
-      y1 = Staff_symbol_referencer::position_f (lh);
-    }
-  else
-    {
-      Grob * lh = first_head (me);
-      if (!lh)
-       return SCM_EOL;
-      y1 = Staff_symbol_referencer::position_f (lh);
-    }
-  
+  Real y1 = Staff_symbol_referencer::get_position (lh);
   Real y2 = stem_end_position (me);
   Real y2 = stem_end_position (me);
-  
+
   Interval stem_y (y1 <? y2,y2 >? y1);
 
 
   Interval stem_y (y1 <? y2,y2 >? y1);
 
 
@@ -668,28 +679,30 @@ Stem::brew_molecule (SCM smob)
        must not take ledgers into account.
        */
       Interval head_height = Note_head::head_extent (hed,Y_AXIS);
        must not take ledgers into account.
        */
       Interval head_height = Note_head::head_extent (hed,Y_AXIS);
-      Real y_attach = Note_head::stem_attachment_coordinate ( hed, Y_AXIS);
+      Real y_attach = Note_head::stem_attachment_coordinate (hed, Y_AXIS);
 
       y_attach = head_height.linear_combination (y_attach);
       stem_y[Direction (-d)] += d * y_attach/dy;
     }
 
       y_attach = head_height.linear_combination (y_attach);
       stem_y[Direction (-d)] += d * y_attach/dy;
     }
-  
-  if (!invisible_b (me))
-    {
-      Real stem_width = gh_scm2double (me->get_grob_property ("thickness"))
-       // URG
-       * me->paper_l ()->get_var ("linethickness");
-      
-      Molecule ss =Lookup::filledbox (Box (Interval (-stem_width/2, stem_width/2),
-                                          Interval (stem_y[DOWN]*dy, stem_y[UP]*dy)));
-      mol.add_molecule (ss);
-    }
 
 
-  if (!beam_l (me) && abs (duration_log (me)) > 2)
+
+  // URG
+  Real stem_width = thickness (me);
+  Real blot =
+       me->get_paper ()->get_dimension (ly_symbol2scm ("blotdiameter"));
+
+  Box b = Box (Interval (-stem_width/2, stem_width/2),
+              Interval (stem_y[DOWN]*dy, stem_y[UP]*dy));
+
+  Stencil ss = Lookup::round_filled_box (b, blot);
+  mol.add_stencil (ss);
+
+  if (!get_beam (me) && abs (duration_log (me)) > 2)
     {
     {
-      Molecule fl = flag (me);
-      fl.translate_axis (stem_y[d]*dy, Y_AXIS);
-      mol.add_molecule (fl);
+      Stencil fl = flag (me);
+      fl.translate_axis (stem_y[d]*dy - d * blot/2, Y_AXIS);
+      fl.translate_axis (stem_width/2, X_AXIS);
+      mol.add_stencil (fl);
     }
 
   return mol.smobbed_copy ();
     }
 
   return mol.smobbed_copy ();
@@ -703,27 +716,26 @@ SCM
 Stem::off_callback (SCM element_smob, SCM)
 {
   Grob *me = unsmob_grob (element_smob);
 Stem::off_callback (SCM element_smob, SCM)
 {
   Grob *me = unsmob_grob (element_smob);
-  
+
   Real r=0;
 
   if (head_count (me) == 0)
     {
   Real r=0;
 
   if (head_count (me) == 0)
     {
-      return gh_double2scm (0.0);
+      return scm_make_real (0.0);
     }
     }
-  
+
   if (Grob * f = first_head (me))
     {
   if (Grob * f = first_head (me))
     {
-      Interval head_wid = Note_head::head_extent(f, X_AXIS);
+      Interval head_wid = Note_head::head_extent (f, X_AXIS);
 
 
-      
       Real attach =0.0;
 
       Real attach =0.0;
 
-      if (invisible_b (me))
+      if (is_invisible (me))
        {
          attach = 0.0;
        }
       else
        {
          attach = 0.0;
        }
       else
-       attach = Note_head::stem_attachment_coordinate(f, X_AXIS);
+       attach = Note_head::stem_attachment_coordinate (f, X_AXIS);
 
       Direction d = get_direction (me);
 
 
       Direction d = get_direction (me);
 
@@ -737,158 +749,189 @@ Stem::off_callback (SCM element_smob, SCM)
       if (attach)
        {
          Real rule_thick
       if (attach)
        {
          Real rule_thick
-           = gh_scm2double (me->get_grob_property ("thickness"))
-           * me->paper_l ()->get_var ("linethickness");
-
-         
+           = thickness (me);
+       
          r += - d * rule_thick * 0.5;
        }
     }
          r += - d * rule_thick * 0.5;
        }
     }
-  return gh_double2scm (r);
+  return scm_make_real (r);
 }
 
 
 }
 
 
-
 Grob*
 Grob*
-Stem::beam_l (Grob*me)
+Stem::get_beam (Grob*me)
 {
 {
-  SCM b=  me->get_grob_property ("beam");
+  SCM b=  me->get_property ("beam");
   return unsmob_grob (b);
 }
 
   return unsmob_grob (b);
 }
 
-// ugh still very long.
 Stem_info
 Stem_info
-Stem::calc_stem_info (Grob*me) 
-{
-  SCM up_to_staff = me->get_grob_property ("up-to-staff");
-  if (gh_scm2bool(up_to_staff)) {
-    
-    // Up-to-staff : the stem end out of the staff.
-
-    /*
-      FIXME: duplicate code.
-     */
-    int line_count = Staff_symbol_referencer::line_count (me);
-    
-    Stem_info si ;
-    
-    Direction dir = get_direction (me);
-    
-    si.ideal_y_ = dir*  (line_count + 1.5);
-    si.dir_ = dir;
-    si.shortest_y_ = si.ideal_y_; 
-    
-    return si;
-  }
-  
-  SCM scm_info = me->get_grob_property ("stem-info");
-
-  if (gh_pair_p (scm_info ))
+Stem::get_stem_info (Grob *me)
+{
+  /* Return cached info if available */
+  SCM scm_info = me->get_property ("stem-info");
+  if (!ly_c_pair_p (scm_info))
     {
     {
-      Stem_info si ;
+      calc_stem_info (me);
+      scm_info = me->get_property ("stem-info");
+    }
+
+  Stem_info si;
+  si.dir_ = get_grob_direction (me);
+  si.ideal_y_ = ly_scm2double (ly_car (scm_info));
+  si.shortest_y_ = ly_scm2double (ly_cadr (scm_info));
+  return si;
+}
 
 
-      si.dir_ = Directional_element_interface::get(me); 
-      si.ideal_y_ = gh_scm2double (gh_car (scm_info)); 
-      si.shortest_y_ = gh_scm2double (gh_cadr (scm_info));
 
 
-      return si;
+/* TODO: add extra space for tremolos!  */
+void
+Stem::calc_stem_info (Grob *me)
+{
+  Direction my_dir = get_grob_direction (me);
+
+  if (!my_dir)
+    {
+      programming_error ("No stem dir set?");
+      my_dir  = UP;
     }
 
     }
 
-  Direction mydir = Directional_element_interface::get (me);
   Real staff_space = Staff_symbol_referencer::staff_space (me);
   Real staff_space = Staff_symbol_referencer::staff_space (me);
-  Real half_space = staff_space / 2;
-
-  Grob * beam = beam_l (me);
-  int beam_count = beam_multiplicity(me).length()+1;
-  Real beam_translation= Beam::get_beam_translation (beam);
-  Real thick = gh_scm2double (beam->get_grob_property ("thickness"));
-  Real note_start = chord_start_y (me);
-  
-  /* from here on, calculate as if dir == UP */
-  note_start *= mydir;
-  
-  SCM grace_prop = me->get_grob_property ("grace");
-  
-  bool grace_b = to_boolean (grace_prop);
-  SCM bml = robust_list_ref ( beam_count ,
-                             me->get_grob_property ("beamed-minimum-lengths"));
-
-  Real minimum_length = gh_scm2double(bml)*staff_space;
-  SCM bl =  robust_list_ref ( beam_count ,
-                             me->get_grob_property ("beamed-lengths"));
-  Real stem_length =  gh_scm2double(bl) * staff_space;
+  Grob *beam = get_beam (me);
+  Real beam_translation = Beam::get_beam_translation (beam);
+  Real beam_thickness = Beam::get_thickness (beam);
+  int beam_count = Beam::get_direction_beam_count (beam, my_dir);
 
 
 
 
-  /*
-    stem goes to center of beam, hence 0.5
-   */
-  Real beam_lengthen = beam_translation* (beam_count - 1)
-    + ((beam_count > 0) ? thick : 0) - 0.5 * thick;
+  /* Simple standard stem length */
+  SCM lengths = me->get_property ("beamed-lengths");
+  Real ideal_length =
+    ly_scm2double (robust_list_ref (beam_count - 1,lengths))
+               
+    * staff_space
+    /* stem only extends to center of beam */
+    - 0.5 * beam_thickness;
 
 
-  Real shortest_y = note_start + minimum_length + beam_lengthen;
-  Real ideal_y = stem_length + note_start + beam_lengthen;
+  /* Condition: sane minimum free stem length (chord to beams) */
+  lengths = me->get_property ("beamed-minimum-free-lengths");
+  Real ideal_minimum_free =
+    ly_scm2double (robust_list_ref (beam_count - 1, lengths))
+    * staff_space;
 
 
-  /*
-    lowest beam of (UP) beam must never be lower than second staffline
 
 
-    Hmm, reference (Wanske?)
+  /* UGH
+     It seems that also for ideal minimum length, we must use
+     the maximum beam count (for this direction):
 
 
-    Although this (additional) rule is probably correct,
-    I expect that highest beam (UP) should also never be lower
-    than middle staffline, just as normal stems.
-       
-  */
-  bool no_extend_b = to_boolean (me->get_grob_property ("no-stem-extend"));
-  if (!grace_b && !no_extend_b)
+     \score{ \notes\relative c''{ [a8 a32] }}
+
+     must be horizontal. */
+  Real height_of_my_beams = beam_thickness
+    + (beam_count - 1) * beam_translation;
+
+  Real ideal_minimum_length = ideal_minimum_free
+    + height_of_my_beams
+    /* stem only extends to center of beam */
+    - 0.5 * beam_thickness;
+
+  ideal_length = ideal_length >? ideal_minimum_length;
+
+  /* Convert to Y position, calculate for dir == UP */
+  Real note_start =
+    /* staff positions */
+    head_positions (me)[my_dir] * 0.5
+    * my_dir * staff_space;
+  Real ideal_y = note_start + ideal_length;
+
+
+  /* Conditions for Y position */
+
+  /* Lowest beam of (UP) beam must never be lower than second staffline
+
+     Reference?
+
+     Although this (additional) rule is probably correct,
+     I expect that highest beam (UP) should also never be lower
+     than middle staffline, just as normal stems.
+
+     Reference?
+
+     Obviously not for grace beams.
+
+     Also, not for knees.  Seems to be a good thing. */
+  bool no_extend_b = to_boolean (me->get_property ("no-stem-extend"));
+  bool is_knee = to_boolean (beam->get_property ("knee"));
+  if (!no_extend_b && !is_knee)
     {
     {
-      /* highest beam of (UP) beam must never be lower than middle
-        staffline
-        lowest beam of (UP) beam must never be lower than second staffline
-      */
-      ideal_y =
-       ideal_y >? 0
-       >? (- 2 * half_space - thick + beam_lengthen);
+      /* Highest beam of (UP) beam must never be lower than middle
+        staffline */
+      ideal_y =        ideal_y >? 0;
+      /* Lowest beam of (UP) beam must never be lower than second staffline */
+      ideal_y =        ideal_y >? (-staff_space
+                           - beam_thickness + height_of_my_beams);
     }
     }
-    
-  
-  //  ideal_y = ideal_y >? shortest_y;
-  SCM s = beam->get_grob_property ("shorten");
-  if (gh_number_p (s))
-    ideal_y -= gh_scm2double (s);
-
-  
-  ideal_y *= mydir;
-  shortest_y *= mydir; 
-  
-  me->set_grob_property ("stem-info",
-                        scm_list_n (gh_double2scm (ideal_y),
-                                    gh_double2scm (shortest_y),
-                                    SCM_UNDEFINED));
 
 
-  Stem_info si;
-  si.dir_ = mydir;
-  si.shortest_y_ = shortest_y;
-  si.ideal_y_ = ideal_y;
-  
-  return si;
-}
 
 
+  ideal_y -= robust_scm2double (beam->get_property ("shorten"), 0);
+
+  Real minimum_free =
+    ly_scm2double (robust_list_ref
+                  (beam_count - 1,
+                   me->get_property
+                   ("beamed-extreme-minimum-free-lengths")))
+    * staff_space;
+
+  Real minimum_length = minimum_free
+    + height_of_my_beams
+    /* stem only extends to center of beam */
+    - 0.5 * beam_thickness;
+
+  ideal_y *= my_dir;
+  Real minimum_y = note_start + minimum_length;
+  Real shortest_y = minimum_y * my_dir;
+
+  me->set_property ("stem-info",
+                   scm_list_2 (scm_make_real (ideal_y),
+                               scm_make_real (shortest_y)));
+}
 
 
-// move to stem?
 Slice
 Stem::beam_multiplicity (Grob *stem)
 {
 Slice
 Stem::beam_multiplicity (Grob *stem)
 {
-  SCM beaming= stem->get_grob_property ("beaming");
-  Slice l = int_list_to_slice (gh_car (beaming));
-  Slice r = int_list_to_slice (gh_cdr (beaming));
-  l.unite (r);
-
-  return l;
+  SCM beaming= stem->get_property ("beaming");
+  Slice le = int_list_to_slice (ly_car (beaming));
+  Slice ri = int_list_to_slice (ly_cdr (beaming));
+  le.unite (ri);
+  return le;
 }
 
 
 }
 
 
+/* FIXME:  Too many properties  */
 ADD_INTERFACE (Stem,"stem-interface",
 ADD_INTERFACE (Stem,"stem-interface",
-  "A stem",
-  "up-to-staff avoid-note-head adjust-if-on-staffline thickness stem-info beamed-lengths beamed-minimum-lengths lengths beam stem-shorten duration-log beaming neutral-direction stem-end-position support-head note-heads direction length style no-stem-extend flag-style dir-forced");
+              "The stem represent the graphical  stem. "
+              "  In addition, it internally connects note heads, beams, tremolos. Rests "
+              " and whole notes have invisible stems.",
+              "tremolo-flag french-beaming "
+              "avoid-note-head thickness "
+              "stem-info beamed-lengths beamed-minimum-free-lengths "
+              "beamed-extreme-minimum-free-lengths lengths beam stem-shorten "
+              "duration-log beaming neutral-direction stem-end-position "
+              "note-heads direction length flag-style "
+              "no-stem-extend stroke-style");
 
 
 
 
+
+/****************************************************************/
+
+Stem_info::Stem_info ()
+{
+  ideal_y_ = shortest_y_ =0;
+  dir_ = CENTER;
+}
+
+void
+Stem_info::scale (Real x)
+{
+  ideal_y_ *= x;
+  shortest_y_ *= x;
+}