]> git.donarmstrong.com Git - lilypond.git/blobdiff - lily/stem.cc
* input/test/improv.ly: update: do not use Thread switching.
[lilypond.git] / lily / stem.cc
index b2ffe3e86eb27b2084cc14319d1dcafe41cc5f50..c4c7ea364f1415bf1b1ad855601b2386dd23fd34 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--2003 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
@@ -19,7 +19,7 @@
 #include "directional-element-interface.hh"
 #include "note-head.hh"
 #include "stem.hh"
 #include "directional-element-interface.hh"
 #include "note-head.hh"
 #include "stem.hh"
-#include "debug.hh"
+#include "warn.hh"
 #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 "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
 
 void
-Stem::set_beaming (Grob*me ,int i,  Direction d)
+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_grob_property ("beaming");
   
   if (!gh_pair_p (pair))
     {
-      pair = gh_cons (gh_int2scm (-1),gh_int2scm (-1));
-      me->      set_grob_property ("beaming", pair);
+      pair = gh_cons (SCM_EOL, SCM_EOL);
+      me->set_grob_property ("beaming", pair);
     }
     }
-  index_set_cell (pair, d, gh_int2scm (i));
-}
 
 
-int
-Stem::beam_count (Grob*me,Direction d)
-{
-  SCM p=me->get_grob_property ("beaming");
-  if (gh_pair_p (p))
-    return gh_scm2int (index_cell (p,d));
-  else
-    return -1;
+  SCM l = index_get_cell (pair, d);
+  for( int i = 0; i<  beam_count; i++)
+    {
+      l = gh_cons (gh_int2scm (i), l);
+    }
+  index_set_cell (pair, d, l);         
 }
 
 }
 
+
 Interval
 Stem::head_positions (Grob*me) 
 {
 Interval
 Stem::head_positions (Grob*me) 
 {
@@ -68,8 +66,8 @@ 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]));
 }
 
 
 }
 
 
@@ -99,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 ;
 }
@@ -126,15 +124,13 @@ Stem::set_stemend (Grob*me, Real se)
 
 /*
   Note head that determines hshift for upstems
 
 /*
   Note head that determines hshift for upstems
- */ 
+
+  WARNING: triggers direction
+*/ 
 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.
@@ -155,7 +151,9 @@ Stem::head_count (Grob*me)
 
 /*
   The note head which forms one end of the stem.  
 
 /*
   The note head which forms one end of the stem.  
- */
+
+  WARNING: triggers direction
+*/
 Grob*
 Stem::first_head (Grob*me)
 {
 Grob*
 Stem::first_head (Grob*me)
 {
@@ -196,7 +194,7 @@ Stem::extremal_heads (Grob*me)
       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 = int (Staff_symbol_referencer::get_position (n));
 
       Direction d = LEFT;
       do {
 
       Direction d = LEFT;
       do {
@@ -224,11 +222,11 @@ Stem::note_head_positions (Grob *me)
   for (SCM s = me->get_grob_property ("note-heads"); gh_pair_p (s); s = ly_cdr (s))
     {
       Grob * n = unsmob_grob (ly_car (s));
   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));
+      int p = int (Staff_symbol_referencer::get_position (n));
 
       ps.push (p);
     }
 
       ps.push (p);
     }
-
+  
   ps.sort (icmp);
   return ps; 
 }
   ps.sort (icmp);
   return ps; 
 }
@@ -240,6 +238,9 @@ Stem::add_head (Grob*me, Grob *n)
   n->set_grob_property ("stem", me->self_scm ());
   n->add_dependency (me);
 
   n->set_grob_property ("stem", me->self_scm ());
   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);
@@ -249,7 +250,8 @@ Stem::add_head (Grob*me, Grob *n)
 bool
 Stem::invisible_b (Grob*me)
 {
 bool
 Stem::invisible_b (Grob*me)
 {
-  return ! (head_count (me) && Note_head::balltype_i (support_head (me)) >= 1);
+  return ! (head_count (me)
+           && gh_scm2int (me->get_grob_property ("duration-log")) >= 1);
 }
 
 Direction
 }
 
 Direction
@@ -257,7 +259,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;
     }
@@ -274,41 +276,30 @@ Stem::get_default_dir (Grob*me)
 Real
 Stem::get_default_stem_end_position (Grob*me) 
 {
 Real
 Stem::get_default_stem_end_position (Grob*me) 
 {
-  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 = 0.;
+  
+  Real length = 7;             // WARNING: IN HALF SPACES
   SCM scm_len = me->get_grob_property ("length");
   if (gh_number_p (scm_len))
     {
   SCM scm_len = me->get_grob_property ("length");
   if (gh_number_p (scm_len))
     {
-      length_f = gh_scm2double (scm_len);
+      length = gh_scm2double (scm_len);
     }
   else
     {
       s = me->get_grob_property ("lengths");
     }
   else
     {
       s = me->get_grob_property ("lengths");
-      for (SCM q = s; q != SCM_EOL; q = ly_cdr (q))
-       a.push (gh_scm2double (ly_car (q)));
-               
-      // stem uses half-spaces
-      length_f = a[ ((duration_log (me) - 2) >? 0) <? (a.size () - 1)] * 2;
+      if (gh_pair_p (s))
+       {
+         length = 2* gh_scm2double (robust_list_ref (durlog -2, s));
+       }
     }
 
 
     }
 
 
-  a.clear ();
-  s = me->get_grob_property ("stem-shorten");
-  for (SCM q = s; gh_pair_p (q); q = ly_cdr (q))
-    a.push (gh_scm2double (ly_car (q)));
-
-
-  // stem uses half-spaces
-
-  // fixme: use scm_list_n_ref () iso. array[]
-  Real shorten_f = a[ ((duration_log (me) - 2) >? 0) <? (a.size () - 1)] * 2;
-
-  /* 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
@@ -317,20 +308,73 @@ 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 (!chord_start_y (me)
+      || (get_direction (me) != get_default_dir (me)))
+    {
+      
+  
+      SCM sshorten = me->get_grob_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)[get_direction (me)]) <= 1)
+       shorten *= 0.5;
+  
+      length -= shorten;
+    }
+
+  /*
+    Tremolo stuff: 
+  */
+  Grob * trem = unsmob_grob (me->get_grob_property ("tremolo-flag"));
+  if (trem &&  !unsmob_grob (me->get_grob_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_molecule() looks at the beam.)
+       
+        --hwn 
+      */
+      
+      Real minlen =
+       1.0 + 2 * Stem_tremolo::raw_molecule (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);  
   Interval hp = head_positions (me);  
-  Real st = hp[dir] + dir * length_f;
+  Real st = hp[dir] + dir * length;
 
 
+  /*
+    TODO: change name  to extend-stems to staff/center/'()
+  */
   bool no_extend_b = to_boolean (me->get_grob_property ("no-stem-extend"));
   bool no_extend_b = to_boolean (me->get_grob_property ("no-stem-extend"));
-  if (!grace_b && !no_extend_b && dir * st < 0) // junkme?
+  if (!no_extend_b && dir * st < 0) // junkme?
     st = 0.0;
 
   /*
     st = 0.0;
 
   /*
@@ -339,18 +383,18 @@ Stem::get_default_stem_end_position (Grob*me)
     good typesetting practice. 
     
   */
     good typesetting practice. 
     
   */
-  if (!beam_l (me) && dir == UP
-      && duration_log (me) > 2)
+  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,
 
          /*
            Very gory: add myself to the X-support of the parent,
@@ -407,13 +451,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->paper_l ()->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++)
@@ -422,24 +461,39 @@ Stem::position_noteheads (Grob*me)
                                X_AXIS);
     }
   
                                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.
+
+               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 (invisible_b(me))
 
              if (invisible_b(me))
-               heads[i]->translate_axis (-thick *2* d , X_AXIS);
+               heads[i]->translate_axis (-thick*(2 - reverse_overlap) * d , X_AXIS);
 
              
             /* TODO:
 
              
             /* TODO:
@@ -483,7 +537,7 @@ Stem::before_line_breaking (SCM smob)
     }
   else
     {
     }
   else
     {
-      me->remove_grob_property ("molecule-callback");
+      me->set_grob_property ("molecule-callback", SCM_EOL);
     }
   
   return SCM_UNSPECIFIED;
     }
   
   return SCM_UNSPECIFIED;
@@ -506,6 +560,12 @@ Stem::height (SCM smob, SCM ax)
   Interval iv;
   if (mol != SCM_EOL)
     iv = unsmob_molecule (mol)->extent (a);
   Interval iv;
   if (mol != SCM_EOL)
     iv = unsmob_molecule (mol)->extent (a);
+  if (Grob *b =get_beam (me))
+    {
+      Direction d = get_direction (me);
+      iv[d] += d * Beam::get_thickness (b) /2.0 ;
+    }
+
   return ly_interval2scm (iv);
 }
 
   return ly_interval2scm (iv);
 }
 
@@ -513,29 +573,26 @@ Stem::height (SCM smob, SCM ax)
 Molecule
 Stem::flag (Grob*me)
 {
 Molecule
 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))
+  /* 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 flag_style_scm = me->get_grob_property ("flag-style");
+  if (gh_symbol_p (flag_style_scm))
     {
     {
-      fstyle = ly_scm2string (fst);
+      flag_style = ly_symbol2string (flag_style_scm);
     }
 
     }
 
-  SCM st = me->get_grob_property ("style");
-  if (gh_symbol_p (st))
-    {
-      style = (ly_scm2string (scm_symbol_to_string (st)));
-    }
-  else
+  if (flag_style == "no-flag")
     {
     {
-      style = "";
+      return Molecule ();
     }
     }
+
   bool adjust = to_boolean (me->get_grob_property ("adjust-if-on-staffline"));
 
   bool adjust = to_boolean (me->get_grob_property ("adjust-if-on-staffline"));
 
-  if (String::compare_i (style, "mensural") == 0)
+  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,
@@ -561,7 +618,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
@@ -569,10 +627,9 @@ Stem::flag (Grob*me)
            
            --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 = (int)rint (Staff_symbol_referencer::get_position (first_head (me)));
+         staffline_offs = Staff_symbol_referencer::on_staffline (me, p) ?
+           "1" : "0";
        }
       else
         {
        }
       else
         {
@@ -583,14 +640,37 @@ 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);
+  Molecule 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_grob_property ("stroke-style");
+  if (gh_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;
+         Molecule 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_molecule (stroke);
+           }
+       }
+    }
+
+  return flag;
 }
 
 MAKE_SCHEME_CALLBACK (Stem,dim_callback,2);
 }
 
 MAKE_SCHEME_CALLBACK (Stem,dim_callback,2);
@@ -599,18 +679,24 @@ 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_grob_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_grob_property ("thickness"))
+    * Staff_symbol_referencer::line_thickness (me);
+}
 
 MAKE_SCHEME_CALLBACK (Stem,brew_molecule,1);
 
 
 MAKE_SCHEME_CALLBACK (Stem,brew_molecule,1);
 
@@ -620,33 +706,26 @@ Stem::brew_molecule (SCM smob)
   Grob*me = unsmob_grob (smob);
   Molecule mol;
   Direction d = get_direction (me);
   Grob*me = unsmob_grob (smob);
   Molecule mol;
   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_grob_property ("avoid-note-head")) 
+    ? last_head (me) :  lh = first_head (me);
 
 
+  if (!lh)
+    return SCM_EOL;
 
 
-  /*
-    TODO: make  the stem start a direction ?
-    
-  */
-  if (to_boolean (me->get_grob_property ("avoid-note-head")))
-    {
-      y1 = Staff_symbol_referencer::position_f (last_head (me));
-    }
-  else
-    {
-      y1 = Staff_symbol_referencer::position_f (first_head (me));
-    }
+  if (invisible_b (me))
+    return SCM_EOL;
   
   
+  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;
@@ -657,27 +736,29 @@ 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);
-    }
+  // URG
+  Real stem_width = thickness (me);
+  Real blot = 
+       me->get_paper ()->get_realvar (ly_symbol2scm ("blotdiameter"));
+  
+  Box b = Box (Interval (-stem_width/2, stem_width/2),
+              Interval (stem_y[DOWN]*dy, stem_y[UP]*dy));
+
+  Molecule ss = Lookup::round_filled_box (b, blot);
+  mol.add_molecule (ss);
 
 
-  if (!beam_l (me) && abs (duration_log (me)) > 2)
+  if (!get_beam (me) && abs (duration_log (me)) > 2)
     {
       Molecule fl = flag (me);
     {
       Molecule fl = flag (me);
-      fl.translate_axis (stem_y[d]*dy, Y_AXIS);
+      fl.translate_axis (stem_y[d]*dy - d * blot/2, Y_AXIS);
+      fl.translate_axis (stem_width/2, X_AXIS);
       mol.add_molecule (fl);
     }
 
       mol.add_molecule (fl);
     }
 
@@ -703,7 +784,6 @@ Stem::off_callback (SCM element_smob, SCM)
   if (Grob * f = first_head (me))
     {
       Interval head_wid = Note_head::head_extent(f, X_AXIS);
   if (Grob * f = first_head (me))
     {
       Interval head_wid = Note_head::head_extent(f, X_AXIS);
-
       
       Real attach =0.0;
 
       
       Real attach =0.0;
 
@@ -726,9 +806,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->paper_l ()->get_var ("linethickness");
-
+           = thickness (me);
          
          r += - d * rule_thick * 0.5;
        }
          
          r += - d * rule_thick * 0.5;
        }
@@ -737,131 +815,180 @@ Stem::off_callback (SCM element_smob, SCM)
 }
 
 
 }
 
 
-
 Grob*
 Grob*
-Stem::beam_l (Grob*me)
+Stem::get_beam (Grob*me)
 {
   SCM b=  me->get_grob_property ("beam");
   return unsmob_grob (b);
 }
 
 {
   SCM b=  me->get_grob_property ("beam");
   return unsmob_grob (b);
 }
 
-
-// ugh still very long.
 Stem_info
 Stem_info
-Stem::calc_stem_info (Grob*me) 
+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_grob_property ("stem-info");
-
-  if (gh_pair_p (scm_info ))
+  if (!gh_pair_p (scm_info))
     {
     {
-      Stem_info 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;
+      calc_stem_info (me);
+      scm_info = me->get_grob_property ("stem-info");
     }
     }
+  
+  Stem_info si;
+  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;
+}
 
 
-  Direction mydir = Directional_element_interface::get (me);
-  Real staff_space = Staff_symbol_referencer::staff_space (me);
-  Real half_space = staff_space / 2;
 
 
-  Grob * beam = beam_l (me);
-  int multiplicity = Beam::get_multiplicity (beam);
-  Real interbeam_f = Beam::get_interbeam (beam);
+/*
+  TODO: add extra space for tremolos!
+ */
+void
+Stem::calc_stem_info (Grob *me)
+{
+  Direction my_dir = get_grob_direction (me);
+  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 = Beam::get_thickness (beam);
+  int beam_count = Beam::get_direction_beam_count (beam, my_dir);
 
 
-  Real thick = gh_scm2double (beam->get_grob_property ("thickness"));
-  
-  Real ideal_y = chord_start_y (me);
-  ideal_y *= mydir;
-  SCM grace_prop = me->get_grob_property ("grace");
 
 
+  /* Simple standard stem length */
+  SCM lengths = me->get_grob_property ("beamed-lengths");
+  Real ideal_length =
+    gh_scm2double (robust_list_ref (beam_count - 1,lengths))
+               
+    * staff_space
+    /* stem only extends to center of beam */
+    - 0.5 * beam_thickness;
   
   
-  bool grace_b = to_boolean (grace_prop);
-  
-  Array<Real> a;
-  SCM s;
+  /* Condition: sane minimum free stem length (chord to beams) */
+  lengths = me->get_grob_property ("beamed-minimum-free-lengths");
+  Real ideal_minimum_free =
+    gh_scm2double (robust_list_ref (beam_count - 1, lengths))
+    * staff_space;
   
   
-  s = me->get_grob_property ("beamed-minimum-lengths");
-  a.clear ();
-  for (SCM q = s; q != SCM_EOL; q = ly_cdr (q))
-    a.push (gh_scm2double (ly_car (q)));
 
 
+  /* UGH
+     It seems that also for ideal minimum length, we must use
+     the maximum beam count (for this direction):
+     
+     \score{ \notes\relative c''{ [a8 a32] }}
+     
+     must be horizontal. */
+  Real height_of_my_beams = beam_thickness
+    + (beam_count - 1) * beam_translation;
 
 
-  Real minimum_length = a[multiplicity <? (a.size () - 1)] * staff_space;
-  s = me->get_grob_property ("beamed-lengths");
-
-  a.clear ();
-  for (SCM q = s; q != SCM_EOL; q = ly_cdr (q))
-    a.push (gh_scm2double (ly_car (q)));
+  Real ideal_minimum_length = ideal_minimum_free
+    + height_of_my_beams
+    /* stem only extends to center of beam */
+    - 0.5 * beam_thickness;
 
 
-  Real stem_length =  a[multiplicity <? (a.size () - 1)] * staff_space;
+  ideal_length = ideal_length >? ideal_minimum_length;
 
 
-  if (multiplicity)
-    ideal_y += thick + (multiplicity - 1) * interbeam_f;
+  
+  /* 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;
 
 
-  Real shortest_y = ideal_y; 
 
 
-  ideal_y += stem_length;
-  shortest_y += minimum_length;
+  /* Conditions for Y position */
 
 
-  /*
-    lowest beam of (UP) beam must never be lower than second staffline
+  /* 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.
 
 
-    Hmm, reference (Wanske?)
+     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.
-       
-  */
+     Obviously not for grace beams.
+     
+     Also, not for knees.  Seems to be a good thing. */
   bool no_extend_b = to_boolean (me->get_grob_property ("no-stem-extend"));
   bool no_extend_b = to_boolean (me->get_grob_property ("no-stem-extend"));
-  if (!grace_b && !no_extend_b)
+  bool knee_b = to_boolean (beam->get_grob_property ("knee"));
+  if (!no_extend_b && !knee_b)
     {
     {
-      /* highest beam of (UP) beam must never be lower than middle
-        staffline
-        lowest beam of (UP) beam must never be lower than second staffline
-      */
-      shortest_y =
-       shortest_y >? 0
-       >? (- 2 * half_space - thick
-           + (multiplicity > 0) * thick
-           + interbeam_f * (multiplicity - 1));
+      /* 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;
 
 
-  s = beam->get_grob_property ("shorten");
-  if (gh_number_p (s))
-    ideal_y -= gh_scm2double (s);
 
 
-  Grob *common = me->common_refpoint (beam, Y_AXIS);
-  Real interstaff_f = mydir *
-    (me->relative_coordinate (common, Y_AXIS)
-     - beam->relative_coordinate (common, Y_AXIS));
-  
-  ideal_y += interstaff_f;
-  shortest_y += interstaff_f;
+  ideal_y -= robust_scm2double (beam->get_grob_property ("shorten"), 0);
+
+  Real minimum_free =
+    gh_scm2double (robust_list_ref
+                  (beam_count - 1,
+                   me->get_grob_property
+                   ("beamed-extreme-minimum-free-lengths")))
+    * staff_space;
 
 
-  ideal_y *= mydir;
-  shortest_y *= mydir; 
+  Real minimum_length = minimum_free
+    + height_of_my_beams
+    /* stem only extends to center of beam */
+    - 0.5 * beam_thickness;
+
+  Real minimum_y = note_start + minimum_length;
+  
+  
+  ideal_y *= my_dir;
+  Real shortest_y = minimum_y * my_dir; 
   
   me->set_grob_property ("stem-info",
                         scm_list_n (gh_double2scm (ideal_y),
                                     gh_double2scm (shortest_y),
                                     SCM_UNDEFINED));
   
   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;
+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;
 }
 
 }
 
+
+/*
+  these are too many props.
+ */
 ADD_INTERFACE (Stem,"stem-interface",
 ADD_INTERFACE (Stem,"stem-interface",
-  "A stem",
-  "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");
+              "A stem",
+              "tremolo-flag french-beaming "
+              "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 "
+              "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;
+}