]> git.donarmstrong.com Git - lilypond.git/blobdiff - lily/stem.cc
* Documentation/user/tutorial.itely: Revised (continued).
[lilypond.git] / lily / stem.cc
index 543fd67c11d15eef4742fe7cc7f019a0448cdded..68c437ab1c0aadd5c99e9e2775f3bfef4c6d8502 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");
+  SCM pair = me->get_property ("beaming");
   
   if (!gh_pair_p (pair))
     {
       pair = gh_cons (SCM_EOL, SCM_EOL);
   
   if (!gh_pair_p (pair))
     {
       pair = gh_cons (SCM_EOL, SCM_EOL);
-      me->set_grob_property ("beaming", pair);
+      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 = gh_cons (gh_int2scm (i), l);
     }
@@ -80,12 +81,12 @@ Stem::chord_start_y (Grob*me)
 Real
 Stem::stem_end_position (Grob*me) 
 {
 Real
 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;
   if (!gh_number_p (p))
     {
       pos = get_default_stem_end_position (me);
   Real pos;
   if (!gh_number_p (p))
     {
       pos = get_default_stem_end_position (me);
-      me->set_grob_property ("stem-end-position", gh_double2scm (pos));
+      me->set_property ("stem-end-position", gh_double2scm (pos));
     }
   else
     pos = gh_scm2double (p);
     }
   else
     pos = gh_scm2double (p);
@@ -96,13 +97,13 @@ Stem::stem_end_position (Grob*me)
 Direction
 Stem::get_direction (Grob*me)
 {
 Direction
 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 ;
 }
@@ -117,7 +118,7 @@ Stem::set_stemend (Grob*me, Real se)
   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", gh_double2scm (se));
 }
 
 
 }
 
 
@@ -129,17 +130,13 @@ Stem::set_stemend (Grob*me, Real se)
 Grob*
 Stem::support_head (Grob*me)
 {
 Grob*
 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)
+  if (head_count (me) == 1)
     {
       /*
        UGH.
        */
       
     {
       /*
        UGH.
        */
       
-      return unsmob_grob (ly_car (me->get_grob_property ("note-heads")));
+      return unsmob_grob (ly_car (me->get_property ("note-heads")));
     }
   else
     return first_head (me);
     }
   else
     return first_head (me);
@@ -192,12 +189,12 @@ Stem::extremal_heads (Grob*me)
   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))
+  for (SCM s = me->get_property ("note-heads"); gh_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::get_position (n));
+      int p = Staff_symbol_referencer::get_rounded_position (n);
 
       Direction d = LEFT;
       do {
 
       Direction d = LEFT;
       do {
@@ -218,18 +215,21 @@ 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"); gh_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::get_position (n));
+      int p = Staff_symbol_referencer::get_rounded_position (n);
 
       ps.push (p);
     }
 
       ps.push (p);
     }
-
+  
   ps.sort (icmp);
   return ps; 
 }
   ps.sort (icmp);
   return ps; 
 }
@@ -238,9 +238,12 @@ Stem::note_head_positions (Grob *me)
 void
 Stem::add_head (Grob*me, Grob *n)
 {
 void
 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))
     {
       Pointer_group_interface::add_grob (me, ly_symbol2scm ("note-heads"), n);
   if (Note_head::has_interface (n))
     {
       Pointer_group_interface::add_grob (me, ly_symbol2scm ("note-heads"), n);
@@ -248,10 +251,10 @@ Stem::add_head (Grob*me, Grob *n)
 }
 
 bool
 }
 
 bool
-Stem::invisible_b (Grob*me)
+Stem::is_invisible (Grob*me)
 {
   return ! (head_count (me)
 {
   return ! (head_count (me)
-           && gh_scm2int (me->get_grob_property ("duration-log")) >= 1);
+           && gh_scm2int (me->get_property ("duration-log")) >= 1);
 }
 
 Direction
 }
 
 Direction
@@ -259,7 +262,7 @@ 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())
+  if (hp.is_empty ())
     {
       return CENTER;
     }
     {
       return CENTER;
     }
@@ -270,52 +273,35 @@ Stem::get_default_dir (Grob*me)
   if (sign (ddistance - udistance))
     return Direction (sign (ddistance -udistance));
 
   if (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
 Stem::get_default_stem_end_position (Grob*me) 
 {
 }
 
 Real
 Stem::get_default_stem_end_position (Grob*me) 
 {
-  /* Tab notation feature: make stem end extend out of staff. */
-  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");
+  
+  Real length = 7;             // WARNING: IN HALF SPACES
+  SCM scm_len = me->get_property ("length");
   if (gh_number_p (scm_len))
     {
   if (gh_number_p (scm_len))
     {
-      length_f = gh_scm2double (scm_len);
+      length = gh_scm2double (scm_len);
     }
   else
     {
     }
   else
     {
-      s = me->get_grob_property ("lengths");
+      s = me->get_property ("lengths");
       if (gh_pair_p (s))
        {
       if (gh_pair_p (s))
        {
-         length_f = 2* gh_scm2double (robust_list_ref (duration_log(me) -2, s));
+         length = 2* gh_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
      'set-default-stemlen' sets direction too
    */
   /* URGURGURG
      'set-default-stemlen' sets direction too
    */
@@ -323,24 +309,69 @@ Stem::get_default_stem_end_position (Grob*me)
   if (!dir)
     {
       dir = get_default_dir (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] */
   /* 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;
+  if (dir * head_positions (me)[dir] >= 0)
+    {
+      SCM sshorten = me->get_property ("stem-shorten");
+      SCM scm_shorten = gh_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;
+    }
 
 
-  Interval hp = head_positions (me);  
-  Real st = hp[dir] + dir * length_f;
+  /*
+    Tremolo stuff: 
+  */
+  Grob * trem = unsmob_grob (me->get_property ("tremolo-flag"));
+  if (trem &&  !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 (trem).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);
+    }
+   
+  Interval hp = head_positions (me);  
+  Real st = hp[dir] + dir * length;
 
   /*
     TODO: change name  to extend-stems to staff/center/'()
   */
 
   /*
     TODO: change name  to extend-stems to staff/center/'()
   */
-  bool no_extend_b = to_boolean (me->get_grob_property ("no-stem-extend"));
-  if (!grace_b && !no_extend_b && dir * st < 0) // junkme?
+  bool no_extend_b = to_boolean (me->get_property ("no-stem-extend"));
+  if (!no_extend_b && dir * st < 0) // junkme?
     st = 0.0;
 
   /*
     st = 0.0;
 
   /*
@@ -350,7 +381,7 @@ Stem::get_default_stem_end_position (Grob*me)
     
   */
   if (!get_beam (me) && dir == UP
     
   */
   if (!get_beam (me) && dir == UP
-      && duration_log (me) > 2)
+      && 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
@@ -360,7 +391,7 @@ Stem::get_default_stem_end_position (Grob*me)
        {
          Real dp = Staff_symbol_referencer::get_position (dots);
          Real flagy =  flag (me).extent (Y_AXIS)[-dir] * 2
        {
          Real dp = Staff_symbol_referencer::get_position (dots);
          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,
 
          /*
            Very gory: add myself to the X-support of the parent,
@@ -397,7 +428,7 @@ Stem::get_default_stem_end_position (Grob*me)
 int
 Stem::duration_log (Grob*me) 
 {
 int
 Stem::duration_log (Grob*me) 
 {
-  SCM s = me->get_grob_property ("duration-log");
+  SCM s = me->get_property ("duration-log");
   return (gh_number_p (s)) ? gh_scm2int (s) : 2;
 }
 
   return (gh_number_p (s)) ? gh_scm2int (s) : 2;
 }
 
@@ -417,13 +448,8 @@ Stem::position_noteheads (Grob*me)
     heads.reverse ();
 
 
     heads.reverse ();
 
 
-  bool invisible = invisible_b (me);
-  Real thick = 0.0;
-  if (invisible)
-        thick = gh_scm2double (me->get_grob_property ("thickness"))
-         * me->get_paper ()->get_var ("linethickness");
+  Real thick = thickness (me);
       
       
-
   Grob *hed = support_head (me);
   Real w = Note_head::head_extent (hed,X_AXIS)[dir];
   for (int i=0; i < heads.size (); i++)
   Grob *hed = support_head (me);
   Real w = Note_head::head_extent (hed,X_AXIS)[dir];
   for (int i=0; i < heads.size (); i++)
@@ -432,24 +458,39 @@ Stem::position_noteheads (Grob*me)
                                X_AXIS);
     }
   
                                X_AXIS);
     }
   
-  bool parity= true;           // todo: make me settable.
-  int lastpos = int (Staff_symbol_referencer::get_position (heads[0]));
+  bool parity= true;
+  Real lastpos = Real (Staff_symbol_referencer::get_position (heads[0]));
   for (int i=1; i < heads.size (); i ++)
     {
       Real p = Staff_symbol_referencer::get_position (heads[i]);
   for (int i=1; i < heads.size (); i ++)
     {
       Real p = Staff_symbol_referencer::get_position (heads[i]);
-      int dy =abs (lastpos- (int)p);
+      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:
@@ -493,7 +534,7 @@ Stem::before_line_breaking (SCM smob)
     }
   else
     {
     }
   else
     {
-      me->set_grob_property ("molecule-callback", SCM_EOL);
+      me->set_property ("print-function", SCM_EOL);
     }
   
   return SCM_UNSPECIFIED;
     }
   
   return SCM_UNSPECIFIED;
@@ -512,10 +553,10 @@ Stem::height (SCM smob, SCM 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);
   if (Grob *b =get_beam (me))
     {
       Direction d = get_direction (me);
@@ -526,25 +567,28 @@ Stem::height (SCM smob, SCM ax)
 }
 
 
 }
 
 
-Molecule
+Stencil
 Stem::flag (Grob*me)
 {
   /* TODO: maybe property stroke-style should take different values,
      e.g. "" (i.e. no stroke), "single" and "double" (currently, it's
      '() or "grace").  */
 Stem::flag (Grob*me)
 {
   /* 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, staffline_offs;
-
-  SCM flag_style_scm = me->get_grob_property ("flag-style");
+  String flag_style;
+  
+  SCM flag_style_scm = me->get_property ("flag-style");
   if (gh_symbol_p (flag_style_scm))
     {
   if (gh_symbol_p (flag_style_scm))
     {
-      flag_style = (ly_scm2string (scm_symbol_to_string (flag_style_scm)));
+      flag_style = ly_symbol2string (flag_style_scm);
     }
     }
-  else
+
+  if (flag_style == "no-flag")
     {
     {
-      flag_style = "";
+      return Stencil ();
     }
     }
-  bool adjust = to_boolean (me->get_grob_property ("adjust-if-on-staffline"));
 
 
+  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
   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
@@ -558,7 +602,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,
@@ -571,7 +615,8 @@ 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
 
          /*
            perhaps the detection whether this correction is needed should
@@ -579,7 +624,7 @@ Stem::flag (Grob*me)
            
            --hwn.
          */
            
            --hwn.
          */
-         int p = (int)rint (Staff_symbol_referencer::get_position (first_head (me)));
+         int p = Staff_symbol_referencer::get_rounded_position (me);
          staffline_offs = Staff_symbol_referencer::on_staffline (me, p) ?
            "1" : "0";
        }
          staffline_offs = Staff_symbol_referencer::on_staffline (me, p) ?
            "1" : "0";
        }
@@ -592,31 +637,32 @@ Stem::flag (Grob*me)
     {
       staffline_offs = "";
     }
     {
       staffline_offs = "";
     }
+
   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);
   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);
-  Molecule flag = fm->find_by_name ("flags-" + font_char);
-  if (flag.empty_b ())
+  Stencil flag = fm->find_by_name ("flags-" + font_char);
+  if (flag.is_empty ())
     {
       me->warning (_f ("flag `%s' not found", font_char));
     }
 
     {
       me->warning (_f ("flag `%s' not found", font_char));
     }
 
-  SCM stroke_style_scm = me->get_grob_property ("stroke-style");
+  SCM stroke_style_scm = me->get_property ("stroke-style");
   if (gh_string_p (stroke_style_scm))
     {
       String stroke_style = ly_scm2string (stroke_style_scm);
   if (gh_string_p (stroke_style_scm))
     {
       String stroke_style = ly_scm2string (stroke_style_scm);
-      if (!stroke_style.empty_b ())
+      if (!stroke_style.is_empty ())
        {
          String font_char = to_string (dir) + stroke_style;
        {
          String font_char = to_string (dir) + stroke_style;
-         Molecule stroke = fm->find_by_name ("flags-" + font_char);
-         if (stroke.empty_b ())
+         Stencil stroke = fm->find_by_name ("flags-" + font_char);
+         if (stroke.is_empty ())
            {
              me->warning (_f ("flag stroke `%s' not found", font_char));
            }
          else
            {
            {
              me->warning (_f ("flag stroke `%s' not found", font_char));
            }
          else
            {
-             flag.add_molecule (stroke);
+             flag.add_stencil (stroke);
            }
        }
     }
            }
        }
     }
@@ -630,62 +676,53 @@ Stem::dim_callback (SCM e, SCM ax)
 {
   Axis a = (Axis) gh_scm2int (ax);
   assert (a == X_AXIS);
 {
   Axis a = (Axis) gh_scm2int (ax);
   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 gh_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;
-
   /*
   /*
-    This is required to avoid stems passing in tablature chords...
-   */
+    TODO: make the stem start a direction ?
 
 
-
-  /*
-    TODO: make  the stem start a direction ?
+    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;
+
+  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::get_position (lh);
-    }
-  else
-    {
-      Grob * lh = first_head (me);
-      if (!lh)
-       return SCM_EOL;
-      y1 = Staff_symbol_referencer::get_position (lh);
-    }
-  
+  Real y1 = Staff_symbol_referencer::get_position (lh);
   Real y2 = stem_end_position (me);
   
   Interval stem_y (y1 <? y2,y2 >? y1);
   Real y2 = stem_end_position (me);
   
   Interval stem_y (y1 <? y2,y2 >? y1);
-
 
   // dy?
   Real dy = Staff_symbol_referencer::staff_space (me) * 0.5;
 
   // dy?
   Real dy = Staff_symbol_referencer::staff_space (me) * 0.5;
@@ -696,28 +733,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->get_paper ()->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);
-    }
+  // 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)
     {
 
   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 ();
@@ -741,17 +780,16 @@ Stem::off_callback (SCM element_smob, SCM)
   
   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);
 
@@ -765,9 +803,7 @@ 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->get_paper ()->get_var ("linethickness");
-
+           = thickness (me);
          
          r += - d * rule_thick * 0.5;
        }
          
          r += - d * rule_thick * 0.5;
        }
@@ -775,10 +811,11 @@ Stem::off_callback (SCM element_smob, SCM)
   return gh_double2scm (r);
 }
 
   return gh_double2scm (r);
 }
 
+
 Grob*
 Stem::get_beam (Grob*me)
 {
 Grob*
 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);
 }
 
@@ -786,62 +823,55 @@ Stem_info
 Stem::get_stem_info (Grob *me)
 {
   /* Return cached info if available */
 Stem::get_stem_info (Grob *me)
 {
   /* Return cached info if available */
-  SCM scm_info = me->get_grob_property ("stem-info");
+  SCM scm_info = me->get_property ("stem-info");
   if (!gh_pair_p (scm_info))
     {
       calc_stem_info (me);
   if (!gh_pair_p (scm_info))
     {
       calc_stem_info (me);
-      scm_info = me->get_grob_property ("stem-info");
+      scm_info = me->get_property ("stem-info");
     }
   
   Stem_info si;
     }
   
   Stem_info si;
-  si.dir_ = Directional_element_interface::get (me); 
+  si.dir_ = get_grob_direction (me); 
   si.ideal_y_ = gh_scm2double (gh_car (scm_info)); 
   si.shortest_y_ = gh_scm2double (gh_cadr (scm_info));
   return si;
 }
 
   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)
 {
 void
 Stem::calc_stem_info (Grob *me)
 {
-  /* Tab notation feature: make stem end extend out of staff. */
-  SCM up_to_staff = me->get_grob_property ("up-to-staff");
-  if (to_boolean (up_to_staff))
+  Direction my_dir = get_grob_direction (me);
+
+  if (!my_dir)
     {
     {
-      int line_count = Staff_symbol_referencer::line_count (me);
-      Direction dir = get_direction (me);
-      Real ideal_y = dir * (line_count + 1.5);
-      Real shortest_y = ideal_y;
-      
-      me->set_grob_property ("stem-info",
-                            scm_list_n (gh_double2scm (ideal_y),
-                                        gh_double2scm (shortest_y),
-                                        SCM_UNDEFINED));
-      return;
+      programming_error ("No stem dir set?");
+      my_dir  = UP;
     }
     }
-
-  Direction my_dir = Directional_element_interface::get (me);
+  
   Real staff_space = Staff_symbol_referencer::staff_space (me);
   Grob *beam = get_beam (me);
   Real beam_translation = Beam::get_beam_translation (beam);
   Real staff_space = Staff_symbol_referencer::staff_space (me);
   Grob *beam = get_beam (me);
   Real beam_translation = Beam::get_beam_translation (beam);
-  Real beam_thickness = gh_scm2double (beam->get_grob_property ("thickness"));
+  Real beam_thickness = Beam::get_thickness (beam);
   int beam_count = Beam::get_direction_beam_count (beam, my_dir);
 
 
   /* Simple standard stem length */
   int beam_count = Beam::get_direction_beam_count (beam, my_dir);
 
 
   /* Simple standard stem length */
+  SCM lengths = me->get_property ("beamed-lengths");
   Real ideal_length =
   Real ideal_length =
-    gh_scm2double (robust_list_ref
-                  (beam_count - 1,
-                   me->get_grob_property ("beamed-lengths")))
+    gh_scm2double (robust_list_ref (beam_count - 1,lengths))
+               
     * staff_space
     /* stem only extends to center of beam */
     - 0.5 * beam_thickness;
     * staff_space
     /* stem only extends to center of beam */
     - 0.5 * beam_thickness;
-
   
   /* Condition: sane minimum free stem length (chord to beams) */
   
   /* Condition: sane minimum free stem length (chord to beams) */
+  lengths = me->get_property ("beamed-minimum-free-lengths");
   Real ideal_minimum_free =
   Real ideal_minimum_free =
-    gh_scm2double (robust_list_ref
-                  (beam_count - 1,
-                   me->get_grob_property ("beamed-minimum-free-lengths")))
+    gh_scm2double (robust_list_ref (beam_count - 1, lengths))
     * staff_space;
   
 
     * staff_space;
   
 
@@ -867,7 +897,7 @@ Stem::calc_stem_info (Grob *me)
   Real note_start =
     /* staff positions */
     head_positions (me)[my_dir] * 0.5
   Real note_start =
     /* staff positions */
     head_positions (me)[my_dir] * 0.5
-    * my_dir;
+    * my_dir * staff_space;
   Real ideal_y = note_start + ideal_length;
 
 
   Real ideal_y = note_start + ideal_length;
 
 
@@ -886,11 +916,9 @@ Stem::calc_stem_info (Grob *me)
      Obviously not for grace beams.
      
      Also, not for knees.  Seems to be a good thing. */
      Obviously not for grace beams.
      
      Also, not for knees.  Seems to be a good thing. */
-  SCM grace = me->get_grob_property ("grace");
-  bool grace_b = to_boolean (grace);
-  bool no_extend_b = to_boolean (me->get_grob_property ("no-stem-extend"));
-  bool knee_b = to_boolean (beam->get_grob_property ("knee"));
-  if (!grace_b && !no_extend_b && !knee_b)
+  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 */
     {
       /* Highest beam of (UP) beam must never be lower than middle
         staffline */
@@ -901,15 +929,12 @@ Stem::calc_stem_info (Grob *me)
     }
 
 
     }
 
 
-  SCM shorten = beam->get_grob_property ("shorten");
-  if (gh_number_p (shorten))
-    ideal_y -= gh_scm2double (shorten);
-
+  ideal_y -= robust_scm2double (beam->get_property ("shorten"), 0);
 
   Real minimum_free =
     gh_scm2double (robust_list_ref
                   (beam_count - 1,
 
   Real minimum_free =
     gh_scm2double (robust_list_ref
                   (beam_count - 1,
-                   me->get_grob_property
+                   me->get_property
                    ("beamed-extreme-minimum-free-lengths")))
     * staff_space;
 
                    ("beamed-extreme-minimum-free-lengths")))
     * staff_space;
 
@@ -924,7 +949,7 @@ Stem::calc_stem_info (Grob *me)
   ideal_y *= my_dir;
   Real shortest_y = minimum_y * my_dir; 
   
   ideal_y *= my_dir;
   Real shortest_y = minimum_y * my_dir; 
   
-  me->set_grob_property ("stem-info",
+  me->set_property ("stem-info",
                         scm_list_n (gh_double2scm (ideal_y),
                                     gh_double2scm (shortest_y),
                                     SCM_UNDEFINED));
                         scm_list_n (gh_double2scm (ideal_y),
                                     gh_double2scm (shortest_y),
                                     SCM_UNDEFINED));
@@ -933,7 +958,7 @@ Stem::calc_stem_info (Grob *me)
 Slice
 Stem::beam_multiplicity (Grob *stem)
 {
 Slice
 Stem::beam_multiplicity (Grob *stem)
 {
-  SCM beaming= stem->get_grob_property ("beaming");
+  SCM beaming= stem->get_property ("beaming");
   Slice l = int_list_to_slice (gh_car (beaming));
   Slice r = int_list_to_slice (gh_cdr (beaming));
   l.unite (r);
   Slice l = int_list_to_slice (gh_car (beaming));
   Slice r = int_list_to_slice (gh_cdr (beaming));
   l.unite (r);
@@ -942,8 +967,37 @@ Stem::beam_multiplicity (Grob *stem)
 }
 
 
 }
 
 
+/*
+  these are too many props.
+ */
 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-free-lengths beamed-extreme-minimum-free-lengths lengths beam stem-shorten duration-log beaming neutral-direction stem-end-position support-head note-heads direction length flag-style no-stem-extend stroke-style");
+              "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;
+}