]> git.donarmstrong.com Git - lilypond.git/commitdiff
Better pure height approximations for stems.
authorMike Solomon <mike@apollinemike.com>
Sat, 27 Aug 2011 14:56:55 +0000 (16:56 +0200)
committerMike Solomon <mike@apollinemike.com>
Sat, 27 Aug 2011 14:56:55 +0000 (16:56 +0200)
This makes several large changes to the Stem grobs' property list.

The following properties are deleted:

stem-end-position
stem-begin-position
length

Now, the only property that controls these three parameters is
Y-extent.

A new function, stem::length, has been added to allow for common
length overrides (i.e. cross-staff-stems) and is in the regtest
stem-length-override.ly.

20 files changed:
input/regression/stem-length-estimation.ly
input/regression/stem-length-override.ly [new file with mode: 0644]
input/regression/stem-pure-height-beamed.ly [new file with mode: 0644]
lily/beam.cc
lily/dot-column.cc
lily/flag.cc
lily/include/item.hh
lily/include/stem.hh
lily/item.cc
lily/note-spacing.cc
lily/staff-spacing.cc
lily/stem-tremolo.cc
lily/stem.cc
lily/tie-formatting-problem.cc
lily/tuplet-bracket.cc
ly/engraver-init.ly
scm/define-grob-properties.scm
scm/define-grobs.scm
scm/flag-styles.scm
scm/output-lib.scm

index fd3c194d7ca4f6cd43420433ad2ad0dfc2b8fafb..797fe160dd7baa5d11eb2e58f4be8cccaac02aee 100644 (file)
@@ -1,8 +1,9 @@
 \version "2.14.0"
 
 \header {
-  texidoc = "Stems with overridden 'length should not confuse height estimation.
-This example should fit snugly on one page.
+  texidoc = "Stems with overridden 'Y-extent should
+not confuse height estimation.  This example should fit snugly
+on one page.
 "
 }
 
@@ -25,7 +26,7 @@ This example should fit snugly on one page.
   \score {
     \new Voice {
       \voiceTwo
-      \override Stem #'length = #0
+      \override Stem #'Y-extent = #'(0.0 . 0.0)
       \repeat unfold 144 a4
     }
     \layout {
diff --git a/input/regression/stem-length-override.ly b/input/regression/stem-length-override.ly
new file mode 100644 (file)
index 0000000..7d0bc2d
--- /dev/null
@@ -0,0 +1,13 @@
+
+\version "2.15.9"
+
+\header {
+  texidoc = "Stem length can be overridden via the function
+stem::length
+"
+}
+
+\relative c' {
+  \override Stem #'Y-extent = #(stem::length 8)
+  e4 f'4
+}
diff --git a/input/regression/stem-pure-height-beamed.ly b/input/regression/stem-pure-height-beamed.ly
new file mode 100644 (file)
index 0000000..28cdc6d
--- /dev/null
@@ -0,0 +1,13 @@
+\version "2.15.9"
+
+\header {
+  texidoc = "Lilypond gets beamed stem pure heights correct
+to avoid outside staff collisions.
+"
+}
+
+{
+  \stemUp
+  gis''8 a bes'' a
+  bes''! a bes''! a
+}
index 4acf18c1439f9aa5699e8d9c23dc68a8204b2c30..1ecd71a88ff5a87027c7aa987c67292a5954196d 100644 (file)
@@ -1549,10 +1549,10 @@ Beam::set_stem_lengths (SCM smob)
         stem_y += thick * 0.5 * get_grob_direction (s);
 
       /*
-        Do set_stemend for invisible stems too, so tuplet brackets
+        Do set_stem_positions for invisible stems too, so tuplet brackets
         have a reference point for sloping
        */
-      Stem::set_stemend (s, 2 * stem_y / staff_space);
+      Stem::set_stem_positions (s, 2 * stem_y / staff_space);
     }
 
   return posns;
index daa318bb4e75cb4a673c9c6fbc8973ade543f14f..46b13811be3c9c20c70826c9b13b195ee60064e0 100644 (file)
@@ -137,7 +137,6 @@ Dot_column::calc_positioning_done (SCM smob)
         {
           Grob *commony = stem->common_refpoint (flag, Y_AXIS);
           Interval y = flag->extent (commony, Y_AXIS) * (2 / ss);
-
           Interval x = flag->extent (commonx, X_AXIS);
 
           boxes.push_back (Box (x, y));
index a349a205d9b50cde98218417cd86a99e1de1432f..5aee4d9d155d9e1f2b1a2b94210f83e2cd895b50 100644 (file)
@@ -67,6 +67,7 @@ Flag::print (SCM smob)
   Grob *me = unsmob_grob (smob);
   Grob *stem = me->get_parent (X_AXIS);
 
+  Direction d = get_grob_direction (stem);
   int log = Stem::duration_log (stem);
   string flag_style;
 
@@ -91,7 +92,8 @@ Flag::print (SCM smob)
     {
       if (adjust)
         {
-          int p = (int) (rint (Stem::stem_end_position (stem)));
+          Real ss = Staff_symbol_referencer::staff_space (me);
+          int p = (int) (rint (stem->extent (stem, Y_AXIS)[d] * 2 / ss));
           staffline_offs
             = Staff_symbol_referencer::on_line (stem, p) ? "0" : "1";
         }
@@ -101,7 +103,7 @@ Flag::print (SCM smob)
   else
     staffline_offs = "";
 
-  char dir = (get_grob_direction (stem) == UP) ? 'u' : 'd';
+  char dir = (d == UP) ? 'u' : 'd';
   string font_char = flag_style
                      + to_string (dir) + staffline_offs + to_string (log);
   Font_metric *fm = Font_interface::get_default_font (me);
@@ -146,10 +148,10 @@ Flag::calc_y_offset (SCM smob)
 
   Real blot
     = me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
-  Real half_space = Staff_symbol_referencer::staff_space (me) * 0.5;
-  Real y2 = robust_scm2double (stem->get_property ("stem-end-position"), 0.0);
 
-  return scm_from_double (y2 * half_space - d * blot / 2);
+  Real y2 = stem->extent (stem, Y_AXIS)[d];
+
+  return scm_from_double (y2 - d * blot / 2);
 }
 
 MAKE_SCHEME_CALLBACK (Flag, calc_x_offset, 1);
index 3fa1fcddc2324502317e95e313915af755e8b2a7..945866632e190866d86f3ef0d76ba5d9b62f1c2c 100644 (file)
@@ -52,6 +52,7 @@ public:
   virtual void handle_prebroken_dependencies ();
   virtual Interval_t<int> spanned_rank_interval () const;
   virtual Interval pure_height (Grob *ref, int start, int end);
+  virtual void cache_pure_height (Interval height);
   DECLARE_GROB_INTERFACE ();
 protected:
   virtual void discretionary_processing ();
index 0e3e0f06d1b7f3a0d4f3ed0384feeee5ec533f8f..c8e0f6939c80a65c997ba4eea098fd82ef1d7591 100644 (file)
@@ -40,16 +40,24 @@ public:
   static void add_head (Grob *me, Grob *n);
   static Stem_info get_stem_info (Grob *);
   static Real chord_start_y (Grob *);
-  static void set_stemend (Grob *, Real);
+  static void set_stem_positions (Grob *, Real);
+  static void cache_pure_height (Grob *, Interval, Interval);
   static Slice beam_multiplicity (Grob *);
   static Direction get_default_dir (Grob *);
   static Real thickness (Grob *);
+  static Real beam_end_corrective (Grob *);
   static int head_count (Grob *);
   static bool is_invisible (Grob *);
   static bool is_normal_stem (Grob *);
   static bool is_cross_staff (Grob *);
   static Interval head_positions (Grob *);
-  static Real stem_end_position (Grob *);
+  static Interval internal_pure_height (Grob *, bool);
+  static Interval internal_height (Grob *, bool);
+  static bool is_valid_stem (Grob *);
+  static Grob *get_reference_head (Grob *);
+  static Real internal_calc_stem_end_position (Grob *, bool);
+  static Real internal_calc_stem_begin_position (Grob *, bool);
+
   DECLARE_GROB_INTERFACE ();
   static void set_spacing_hints (Grob *);
   static Grob *flag (Grob *);
@@ -59,9 +67,10 @@ public:
   DECLARE_SCHEME_CALLBACK (offset_callback, (SCM element));
   DECLARE_SCHEME_CALLBACK (calc_direction, (SCM));
   DECLARE_SCHEME_CALLBACK (calc_beaming, (SCM));
-  DECLARE_SCHEME_CALLBACK (calc_length, (SCM));
   DECLARE_SCHEME_CALLBACK (calc_stem_begin_position, (SCM));
+  DECLARE_SCHEME_CALLBACK (pure_calc_stem_begin_position, (SCM, SCM, SCM));
   DECLARE_SCHEME_CALLBACK (calc_stem_end_position, (SCM));
+  DECLARE_SCHEME_CALLBACK (pure_calc_stem_end_position, (SCM, SCM, SCM));
   DECLARE_SCHEME_CALLBACK (calc_stem_info, (SCM));
   DECLARE_SCHEME_CALLBACK (calc_positioning_done, (SCM));
   DECLARE_SCHEME_CALLBACK (width, (SCM smob));
index bf67f3c6dda4d578255a7a3f211c6e28d54c66c1..6a468a6830607f6522f786c7dc15cfefdebdd5b8 100644 (file)
@@ -245,11 +245,17 @@ Item::pure_height (Grob *g, int start, int end)
   if (cached_pure_height_valid_)
     return cached_pure_height_ + pure_relative_y_coordinate (g, start, end);
 
-  cached_pure_height_ = Grob::pure_height (this, start, end);
-  cached_pure_height_valid_ = true;
+  cache_pure_height (Grob::pure_height (this, start, end));
   return cached_pure_height_ + pure_relative_y_coordinate (g, start, end);
 }
 
+void
+Item::cache_pure_height (Interval height)
+{
+  cached_pure_height_ = height;
+  cached_pure_height_valid_ = true;
+}
+
 ADD_INTERFACE (Item,
                "Grobs can be distinguished in their role in the horizontal"
                " spacing.  Many grobs define constraints on the spacing by"
index 9a71c00a598b9bee8aa42cf8de12769fb6d0470e..5cffd5d2053333df09d6782217b10fff2eeb929a 100644 (file)
@@ -31,6 +31,7 @@
 #include "separation-item.hh"
 #include "spacing-interface.hh"
 #include "staff-spacing.hh"
+#include "staff-symbol-referencer.hh"
 #include "stem.hh"
 #include "warn.hh"
 
@@ -273,17 +274,8 @@ Note_spacing::stem_dir_correction (Grob *me, Item *rcolumn,
           Interval hp = Stem::head_positions (stem);
           if (!hp.is_empty ())
             {
-              Real chord_start = hp[stem_dir];
-
-              /*
-                can't look at stem-end-position, since that triggers
-                beam slope computations.
-              */
-              Real stem_end = hp[stem_dir]
-                              + stem_dir * robust_scm2double (stem->get_property ("length"), 7);
-
-              stem_posns[d] = Interval (min (chord_start, stem_end),
-                                        max (chord_start, stem_end));
+              Real ss = Staff_symbol_referencer::staff_space (stem);
+              stem_posns[d] = stem->pure_height (stem, 0, INT_MAX) * (2 / ss);
               head_posns[d].unite (hp);
             }
         }
index 7c4ece46433c5f6be6477a52bc0d3d492d331f3f..6a509d391e2961d1f452588f1bc1c35eef1dee69 100644 (file)
@@ -56,17 +56,7 @@ Staff_spacing::optical_correction (Grob *me, Grob *g, Interval bar_height)
       Direction d = get_grob_direction (stem);
       if (Stem::is_normal_stem (stem) && d == DOWN)
         {
-
-          /*
-            can't look at stem-end-position, since that triggers
-            beam slope computations.
-          */
-          Real stem_start = Stem::head_positions (stem) [d];
-          Real stem_end = stem_start
-                          + d * robust_scm2double (stem->get_property ("length"), 7);
-
-          Interval stem_posns (min (stem_start, stem_end),
-                               max (stem_end, stem_start));
+          Interval stem_posns = stem->pure_height (stem, 0, INT_MAX);
 
           stem_posns.intersect (bar_height);
 
index 2036d9a89072e2ef8019761674ebabd45b8d8c23..cc4589827e69d13b37da34030d98ca5a5fe1658c 100644 (file)
@@ -216,10 +216,9 @@ Stem_tremolo::translated_stencil (Grob *me, Real slope)
   Real beam_translation = get_beam_translation (me);
 
   int beam_count = beam ? (Stem::beam_multiplicity (stem).length () + 1) : 0;
-  Real ss = Staff_symbol_referencer::staff_space (me);
 
   Real end_y
-    = Stem::stem_end_position (stem) * ss / 2
+    = stem->extent (stem, Y_AXIS)[stemdir]
       - stemdir * max (beam_count, 1) * beam_translation;
 
   if (!beam && Stem::duration_log (stem) >= 3)
@@ -234,6 +233,7 @@ Stem_tremolo::translated_stencil (Grob *me, Real slope)
     {
       /* we shouldn't position relative to the end of the stem since the stem
          is invisible */
+      Real ss = Staff_symbol_referencer::staff_space (me);
       vector<int> nhp = Stem::note_head_positions (stem);
       Real note_head = (stemdir == UP ? nhp.back () : nhp[0]) * ss / 2;
       end_y = note_head + stemdir * 1.5;
index d4456817c6023915c5efe3b716803f01b22c2247..7245e3c07fae7053aa0d4d9e0edf4378d42281d9 100644 (file)
   along with LilyPond.  If not, see <http://www.gnu.org/licenses/>.
 */
 
+/*
+  Note that several internal functions have a calc_beam bool argument.
+  This argument means: "If set, acknowledge the fact that there is a beam
+  and deal with it.  If not, give me the measurements as if there is no beam."
+  Most pure functions are called WITHOUT calc_beam, whereas non-pure functions
+  are called WITH calc_beam.
+
+  The only exception to this is ::pure_height, which calls internal_pure_height
+  with "true" for calc_beam in order to trigger the calculations of other
+  pure heights in case there is a beam.  It passes false, however, to
+  internal_height and internal_pure_height for all subsequent iterations.
+*/
+
 #include "stem.hh"
 #include "spanner.hh"
 
@@ -105,15 +118,46 @@ Stem::chord_start_y (Grob *me)
 }
 
 void
-Stem::set_stemend (Grob *me, Real se)
+Stem::set_stem_positions (Grob *me, Real se)
 {
   // todo: margins
   Direction d = get_grob_direction (me);
 
+  Grob *beam = unsmob_grob (me->get_object ("beam"));
   if (d && d * head_positions (me)[get_grob_direction (me)] >= se * d)
     me->warning (_ ("weird stem size, check for narrow beams"));
 
-  me->set_property ("stem-end-position", scm_from_double (se));
+  Interval height = me->pure_height (me, 0, INT_MAX);
+  Real half_space = Staff_symbol_referencer::staff_space (me) * 0.5;
+
+  height[d] = se * half_space + beam_end_corrective (me);
+
+  Real stemlet_length = robust_scm2double (me->get_property ("stemlet-length"),
+                                           0.0);
+  bool stemlet = stemlet_length > 0.0;
+
+  Grob *lh = get_reference_head (me);
+
+  if (!lh)
+    {
+      if (stemlet && beam)
+        {
+          Real beam_translation = Beam::get_beam_translation (beam);
+          Real beam_thickness = Beam::get_beam_thickness (beam);
+          int beam_count = beam_multiplicity (me).length () + 1;
+
+          height[-d] = (height[d] - d
+                        * (0.5 * beam_thickness
+                        + beam_translation * max (0, (beam_count - 1))
+                        + stemlet_length));
+        }
+      else if (!stemlet && beam)
+        height[-d] = height[d];
+      else if (stemlet && !beam)
+        me->programming_error ("Can't have a stemlet without a beam.");
+    }
+
+  me->set_property ("Y-extent", ly_interval2scm (height));
 }
 
 /* Note head that determines hshift for upstems
@@ -241,47 +285,60 @@ Stem::pure_height (SCM smob,
                    SCM /* end */)
 {
   Grob *me = unsmob_grob (smob);
-  Interval iv;
+  return ly_interval2scm (internal_pure_height (me, true));
+}
 
+Interval
+Stem::internal_pure_height (Grob *me, bool calc_beam)
+{
   if (!is_normal_stem (me))
-    return ly_interval2scm (iv);
+    return Interval (0.0, 0.0);
 
-  Real ss = Staff_symbol_referencer::staff_space (me);
-  Real rad = Staff_symbol_referencer::staff_radius (me);
+  Grob *beam = unsmob_grob (me->get_object ("beam"));
+
+  Interval iv = internal_height (me, false);
 
-  if (!to_boolean (me->get_property ("cross-staff")))
+  if (!beam)
+    return iv;
+  if (!to_boolean (me->get_property ("cross-staff")) && calc_beam)
     {
-      Real len_in_halfspaces;
-      SCM user_set_len_scm = me->get_property_data ("length");
-      if (scm_is_number (user_set_len_scm))
-        len_in_halfspaces = scm_to_double (user_set_len_scm);
-      else
-        len_in_halfspaces = scm_to_double (calc_length (smob));
-      Real len = len_in_halfspaces * ss / 2;
+      Interval overshoot;
       Direction dir = get_grob_direction (me);
+      Direction d = DOWN;
+      do
+        overshoot[d] = d == dir ? dir * infinity_f : iv[d];
+      while (flip (&d) != DOWN);
 
-      Interval hp = head_positions (me);
-      if (dir == UP)
-        iv = Interval (0, len);
-      else
-        iv = Interval (-len, 0);
+      vector<Interval> heights;
+      vector<Grob *> my_stems;
+      extract_grob_set (beam, "normal-stems", normal_stems);
+      for (vsize i = 0; i < normal_stems.size (); i++)
+        if (normal_stems[i] != me && get_grob_direction (normal_stems[i]) == dir)
+          {
+            heights.push_back (Stem::internal_pure_height (normal_stems[i], false));
+            my_stems.push_back (normal_stems[i]);
+            iv.unite (heights.back ());
+          }
+      for (vsize i = 0; i < my_stems.size (); i++)
+        cache_pure_height (my_stems[i], iv, heights[i]);
+      iv.intersect (overshoot);
+    }
 
-      if (!hp.is_empty ())
-        {
-          iv.translate (hp[dir] * ss / 2);
-          iv.add_point (hp[-dir] * ss / 2);
-        }
+  return iv;
+}
 
-      /* extend the stem (away from the head) to cover the staff */
-      if (dir == UP)
-        iv[UP] = max (iv[UP], rad * ss);
-      else
-        iv[DOWN] = min (iv[DOWN], -rad * ss);
-    }
-  else
-    iv = Interval (-rad * ss, rad * ss);
+void
+Stem::cache_pure_height (Grob *me, Interval iv, Interval my_iv)
+{
+  Interval overshoot;
+  Direction dir = get_grob_direction (me);
+  Direction d = DOWN;
+  do
+    overshoot[d] = d == dir ? dir * infinity_f : my_iv[d];
+  while (flip (&d) != DOWN);
 
-  return ly_interval2scm (iv);
+  iv.intersect (overshoot);
+  dynamic_cast<Item *> (me)->cache_pure_height (iv);
 }
 
 MAKE_SCHEME_CALLBACK (Stem, calc_stem_end_position, 1)
@@ -289,44 +346,39 @@ SCM
 Stem::calc_stem_end_position (SCM smob)
 {
   Grob *me = unsmob_grob (smob);
+  return scm_from_double (internal_calc_stem_end_position (me, true));
+}
+
+MAKE_SCHEME_CALLBACK (Stem, pure_calc_stem_end_position, 3)
+SCM
+Stem::pure_calc_stem_end_position (SCM smob,
+                                   SCM, /* start */
+                                   SCM /* end */)
+{
+  Grob *me = unsmob_grob (smob);
+  return scm_from_double (internal_calc_stem_end_position (me, false));
+}
 
+Real
+Stem::internal_calc_stem_end_position (Grob *me, bool calc_beam)
+{
   if (!head_count (me))
-    return scm_from_double (0.0);
+    return 0.0;
 
-  if (Grob *beam = get_beam (me))
+  Grob *beam = get_beam (me);
+  Real ss = Staff_symbol_referencer::staff_space (me);
+  if (beam && calc_beam)
     {
       (void) beam->get_property ("quantized-positions");
-      return me->get_property ("stem-end-position");
+      return me->extent (me, Y_AXIS)[get_grob_direction (me)] * ss * 2;
     }
 
   vector<Real> a;
 
   /* WARNING: IN HALF SPACES */
-  Real length = robust_scm2double (me->get_property ("length"), 7);
-
-  Direction dir = get_grob_direction (me);
-  Interval hp = head_positions (me);
-  Real stem_end = dir ? hp[dir] + dir * length : 0;
-
-  /* TODO: change name  to extend-stems to staff/center/'()  */
-  bool no_extend = to_boolean (me->get_property ("no-stem-extend"));
-  if (!no_extend && dir * stem_end < 0)
-    stem_end = 0.0;
-
-  return scm_from_double (stem_end);
-}
-
-/* Length is in half-spaces (or: positions) here. */
-MAKE_SCHEME_CALLBACK (Stem, calc_length, 1)
-SCM
-Stem::calc_length (SCM smob)
-{
-  Grob *me = unsmob_grob (smob);
-
   SCM details = me->get_property ("details");
   int durlog = duration_log (me);
 
-  Real ss = Staff_symbol_referencer::staff_space (me);
   Real staff_rad = Staff_symbol_referencer::staff_radius (me);
   Real length = 7;
   SCM s = ly_assoc_get (ly_symbol2scm ("lengths"), details, SCM_EOL);
@@ -370,7 +422,7 @@ Stem::calc_length (SCM smob)
 
   /* Tremolo stuff.  */
   Grob *t_flag = unsmob_grob (me->get_object ("tremolo-flag"));
-  if (t_flag && !unsmob_grob (me->get_object ("beam")))
+  if (t_flag && (!unsmob_grob (me->get_object ("beam")) || !calc_beam))
     {
       /* Crude hack: add extra space if tremolo flag is there.
 
@@ -398,8 +450,16 @@ Stem::calc_length (SCM smob)
       length = max (length, minlen + 1.0);
     }
 
-  return scm_from_double (length);
+  Real stem_end = dir ? hp[dir] + dir * length : 0;
+
+  /* TODO: change name  to extend-stems to staff/center/'()  */
+  bool no_extend = to_boolean (me->get_property ("no-stem-extend"));
+  if (!no_extend && dir * stem_end < 0)
+    stem_end = 0.0;
+
+  return stem_end;
 }
+
 /* The log of the duration (Number of hooks on the flag minus two)  */
 int
 Stem::duration_log (Grob *me)
@@ -557,29 +617,29 @@ Stem::calc_default_direction (SCM smob)
   return scm_from_int (dir);
 }
 
+// note - height property necessary to trigger quantized beam positions
+// otherwise, we could just use Grob::stencil_height_proc
 MAKE_SCHEME_CALLBACK (Stem, height, 1);
 SCM
 Stem::height (SCM smob)
 {
   Grob *me = unsmob_grob (smob);
-  if (!is_normal_stem (me))
-    return ly_interval2scm (Interval ());
-
-  Direction dir = get_grob_direction (me);
+  return ly_interval2scm (internal_height (me, true));
+}
 
-  Grob *beam = get_beam (me);
-  if (beam)
-    {
-      /* trigger set-stem-lengths. */
-      beam->get_property ("quantized-positions");
-    }
+Grob*
+Stem::get_reference_head (Grob *me)
+{
+  return to_boolean (me->get_property ("avoid-note-head"))
+         ? last_head (me)
+         : first_head (me);
+}
 
-  /*
-    Can't get_stencil (), since that would cache stencils too early.
-    This causes problems with beams.
-   */
-  Stencil *stencil = unsmob_stencil (print (smob));
-  Interval iv = stencil ? stencil->extent (Y_AXIS) : Interval ();
+Real
+Stem::beam_end_corrective (Grob *me)
+{
+  Grob *beam = unsmob_grob (me->get_object ("beam"));
+  Direction dir = get_grob_direction (me);
   if (beam)
     {
       if (dir == CENTER)
@@ -587,16 +647,36 @@ Stem::height (SCM smob)
           programming_error ("no stem direction");
           dir = UP;
         }
-      iv[dir] += dir * Beam::get_beam_thickness (beam) * 0.5;
+      return dir * Beam::get_beam_thickness (beam) * 0.5;
     }
-
-  return ly_interval2scm (iv);
+  return 0.0;
 }
 
-Real
-Stem::stem_end_position (Grob *me)
+Interval
+Stem::internal_height (Grob *me, bool calc_beam)
 {
-  return robust_scm2double (me->get_property ("stem-end-position"), 0);
+  if (!is_valid_stem (me))
+    return Interval ();
+
+  Direction dir = get_grob_direction (me);
+
+  Grob *beam = get_beam (me);
+  if (beam && calc_beam)
+    {
+      /* trigger set-stem-lengths. */
+      (void) beam->get_property ("quantized-positions");
+      return me->extent (me, Y_AXIS);
+    }
+
+  Real y2 = internal_calc_stem_end_position (me, calc_beam);
+  Real y1 = internal_calc_stem_begin_position (me, calc_beam);
+
+  Real half_space = Staff_symbol_referencer::staff_space (me) * 0.5;
+
+  Interval stem_y  = Interval (min (y1, y2), max (y2, y1)) * half_space;
+  stem_y[dir] += beam_end_corrective (me);
+
+  return stem_y;
 }
 
 MAKE_SCHEME_CALLBACK (Stem, width, 1);
@@ -630,12 +710,32 @@ SCM
 Stem::calc_stem_begin_position (SCM smob)
 {
   Grob *me = unsmob_grob (smob);
+  return scm_from_double (internal_calc_stem_begin_position (me, true));
+}
+
+MAKE_SCHEME_CALLBACK (Stem, pure_calc_stem_begin_position, 3);
+SCM
+Stem::pure_calc_stem_begin_position (SCM smob,
+                                     SCM, /* start */
+                                     SCM /* end */)
+{
+  Grob *me = unsmob_grob (smob);
+  return scm_from_double (internal_calc_stem_begin_position (me, false));
+}
+
+Real
+Stem::internal_calc_stem_begin_position (Grob *me, bool calc_beam)
+{
+  Grob *beam = get_beam (me);
+  Real ss = Staff_symbol_referencer::staff_space (me);
+  if (beam && calc_beam)
+    {
+      (void) beam->get_property ("quantized-positions");
+      return me->extent (me, Y_AXIS)[-get_grob_direction (me)] * ss * 2;
+    }
+
   Direction d = get_grob_direction (me);
-  Real half_space = Staff_symbol_referencer::staff_space (me) * 0.5;
-  Grob *lh
-    = to_boolean (me->get_property ("avoid-note-head"))
-      ? last_head (me)
-      : first_head (me);
+  Grob *lh = get_reference_head (me);
 
   Real pos = Staff_symbol_referencer::get_position (lh);
 
@@ -645,64 +745,51 @@ Stem::calc_stem_begin_position (SCM smob)
       Real y_attach = Note_head::stem_attachment_coordinate (head, Y_AXIS);
 
       y_attach = head_height.linear_combination (y_attach);
-      pos += d * y_attach / half_space;
+      pos += d * y_attach * 2 / ss;
     }
 
-  return scm_from_double (pos);
+  return pos;
 }
 
-MAKE_SCHEME_CALLBACK (Stem, print, 1);
-SCM
-Stem::print (SCM smob)
+bool
+Stem::is_valid_stem (Grob *me)
 {
-  Grob *me = unsmob_grob (smob);
-  Grob *beam = get_beam (me);
-
-  Stencil mol;
-  Direction d = get_grob_direction (me);
-
+  /* TODO: make the stem start a direction ?
+     This is required to avoid stems passing in tablature chords.  */
   Real stemlet_length = robust_scm2double (me->get_property ("stemlet-length"),
                                            0.0);
   bool stemlet = stemlet_length > 0.0;
 
-  /* 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)
-      : first_head (me);
+  Grob *lh = get_reference_head (me);
+  Grob *beam = unsmob_grob (me->get_object ("beam"));
 
   if (!lh && !stemlet)
-    return SCM_EOL;
+    return false;
 
   if (!lh && stemlet && !beam)
-    return SCM_EOL;
+    return false;
 
   if (lh && robust_scm2int (lh->get_property ("duration-log"), 0) < 1)
-    return SCM_EOL;
+    return false;
 
   if (is_invisible (me))
-    return SCM_EOL;
+    return false;
 
-  Real y2 = robust_scm2double (me->get_property ("stem-end-position"), 0.0);
-  Real y1 = y2;
-  Real half_space = Staff_symbol_referencer::staff_space (me) * 0.5;
+  return true;
+}
 
-  if (lh)
-    y2 = robust_scm2double (me->get_property ("stem-begin-position"), 0.0);
-  else if (stemlet)
-    {
-      Real beam_translation = Beam::get_beam_translation (beam);
-      Real beam_thickness = Beam::get_beam_thickness (beam);
-      int beam_count = beam_multiplicity (me).length () + 1;
-
-      y2 -= d
-            * (0.5 * beam_thickness
-               + beam_translation * max (0, (beam_count - 1))
-               + stemlet_length) / half_space;
-    }
+MAKE_SCHEME_CALLBACK (Stem, print, 1);
+SCM
+Stem::print (SCM smob)
+{
+  Grob *me = unsmob_grob (smob);
+  if (!is_valid_stem (me))
+    return SCM_EOL;
+
+  Interval stem_y = me->extent (me, Y_AXIS);
+  Direction dir = get_grob_direction (me);
 
-  Interval stem_y (min (y1, y2), max (y2, y1));
+  stem_y[dir] -= beam_end_corrective (me);
 
   // URG
   Real stem_width = thickness (me);
@@ -710,8 +797,9 @@ Stem::print (SCM smob)
     = me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
 
   Box b = Box (Interval (-stem_width / 2, stem_width / 2),
-               Interval (stem_y[DOWN] * half_space, stem_y[UP] * half_space));
+               stem_y);
 
+  Stencil mol;
   Stencil ss = Lookup::round_filled_box (b, blot);
   mol.add_stencil (ss);
 
@@ -990,7 +1078,6 @@ ADD_INTERFACE (Stem,
                "duration-log "
                "flag "
                "french-beaming "
-               "length "
                "length-fraction "
                "max-beam-connect "
                "neutral-direction "
@@ -998,8 +1085,6 @@ ADD_INTERFACE (Stem,
                "note-heads "
                "positioning-done "
                "rests "
-               "stem-begin-position "
-               "stem-end-position "
                "stem-info "
                "stemlet-length "
                "thickness "
index b157d4794c114f9845c24978fe84d182592e2ab0..098bf00a11bfd40f81651f845dbcc1e62a27fb3c 100644 (file)
@@ -149,8 +149,9 @@ Tie_formatting_problem::set_column_chord_outline (vector<Item *> bounds,
           else
             {
               if (use_horizontal_spacing_ || !Stem::get_beam (stem))
-                stem_end_position = Stem::stem_end_position (stem) * staff_space * .5;
+                stem_end_position = stem->extent (stem, Y_AXIS)[get_grob_direction (stem)];
               else
+                // May want to change this to the stem's pure height...
                 stem_end_position = Stem::note_head_positions (stem)[get_grob_direction (stem)]
                                     * staff_space * .5;
             }
index c6dfa799924a5c4b169406cf26854c1b865d0f24..67a00b4b2b86af0f8e46c617e78b25d02745271a 100644 (file)
@@ -553,18 +553,12 @@ Tuplet_bracket::calc_position_and_height (Grob *me_grob, Real *offset, Real *dy)
       && Note_column::get_stem (columns[0])
       && Note_column::get_stem (columns.back ()))
     {
-      /*
-        trigger set_stem_ends
-      */
-      (void) par_beam->get_property ("quantized-positions");
-
       Drul_array<Grob *> stems (Note_column::get_stem (columns[0]),
                                 Note_column::get_stem (columns.back ()));
 
-      Real ss = 0.5 * Staff_symbol_referencer::staff_space (me);
-      Real lp = ss * robust_scm2double (stems[LEFT]->get_property ("stem-end-position"), 0.0)
+      Real lp = stems[LEFT]->extent (stems[LEFT], Y_AXIS)[get_grob_direction (stems[LEFT])]
                 + stems[LEFT]->get_parent (Y_AXIS)->relative_coordinate (commony, Y_AXIS);
-      Real rp = ss * robust_scm2double (stems[RIGHT]->get_property ("stem-end-position"), 0.0)
+      Real rp = stems[RIGHT]->extent (stems[RIGHT], Y_AXIS)[get_grob_direction (stems[RIGHT])]
                 + stems[RIGHT]->get_parent (Y_AXIS)->relative_coordinate (commony, Y_AXIS);
 
       *dy = rp - lp;
index aab1317a9083cc87fb7cc07ae2cd7416e85cece6..a2ba6551f678d11061d13afae9d6bcc90a6946a1 100644 (file)
@@ -782,7 +782,6 @@ context."
   \remove "Accidental_engraver"
   %% make the Stems as short as possible to minimize their influence
   %% on the slur::calc-control-points routine
-  \override Stem #'length = #0
   \override Stem #'no-stem-extend = ##t
   \override Flag #'style = #'no-flag
   \override Stem #'details = #'((lengths 0 0 0 0 0 0)
index 11493d87e4eadf8066988277050dc5bec7c992aa..c8619d9afe4e33da418d11e231a980bd41ef022c 100644 (file)
@@ -836,10 +836,6 @@ the @code{staff-staff-spacing} property of the staff's
 structure.")
      (stem-attachment ,number-pair? "An @code{(@var{x} . @var{y})}
 pair where the stem attaches to the notehead.")
-     (stem-begin-position ,number? "Where does the stem begin (the
-position of the support-head)?")
-     (stem-end-position ,number? "Where does the stem end (the end is
-opposite to the support-head)?")
      ;;[TODO: doco]
      (stem-spacing-correction ,number? "Optical correction amount for
 stems that are placed in tight configurations.  For opposite
index 4beeb04dbf6c9d60433ea19ccdcd1096f3a71d0b..bbb750fbe4f06836638a013cbdfc853e689103f3 100644 (file)
 
        (direction . ,ly:stem::calc-direction)
        (duration-log . ,stem::calc-duration-log)
-       (length . ,ly:stem::calc-length)
        (neutral-direction . ,DOWN)
        (positioning-done . ,ly:stem::calc-positioning-done)
-       (stem-begin-position . ,ly:stem::calc-stem-begin-position)
-       (stem-end-position . ,ly:stem::calc-stem-end-position)
        (stem-info . ,ly:stem::calc-stem-info)
        (stencil . ,ly:stem::print)
        (thickness . 1.3)
     (,ly:side-position-interface::y-aligned-side . ,ly:side-position-interface::pure-y-aligned-side)
     (,ly:slur::height . ,ly:slur::pure-height)
     (,ly:slur::outside-slur-callback . ,ly:slur::pure-outside-slur-callback)
+    (,ly:stem::calc-stem-end-position . ,ly:stem::pure-calc-stem-end-position)
     (,ly:stem::height . ,ly:stem::pure-height)
     (,ly:system::height . ,ly:system::calc-pure-height)))
 
index bce6cab5db494f2811a0782fa3943170874376e6..018ebf50748c76d052aff8ad87e7db91007311ed 100644 (file)
@@ -188,7 +188,14 @@ a flag always touches a staff line."
 
   (let* ((stem-grob (ly:grob-parent grob X))
          (adjust #t)
-         (stem-end (inexact->exact (round (ly:grob-property stem-grob 'stem-end-position))))
+         (d (ly:grob-property stem-grob 'direction))
+         (ss (ly:staff-symbol-staff-space stem-grob))
+         (stem-end (inexact->exact (round (* (index-cell
+                                               (ly:grob-extent stem-grob
+                                                               stem-grob
+                                                               Y)
+                                               d)
+                                             (/ 2 ss)))))
          ; For some reason the stem-end is a real instead of an integer...
          (dir-modifier (if (ly:position-on-line? stem-grob stem-end) "1" "0"))
          (modifier (if adjust dir-modifier "2")))
index 025590e5bae0e001db8f089a96b811485faa1198..e76c46cdb18ddbfade41573a913de1b5dd889671 100644 (file)
   (ly:duration-log
    (ly:event-property (event-cause grob) 'duration)))
 
+(define-public (stem::length val)
+  (lambda (grob)
+    (let* ((d (ly:grob-property grob 'direction))
+           (ss (ly:staff-symbol-staff-space grob))
+           (beg (ly:stem::calc-stem-begin-position grob))
+           (y1 (* beg (* 0.5 ss)))
+           (y2 (* ((if (eqv? d DOWN) - +) beg val) (* 0.5 ss))))
+      (if (eqv? d DOWN)
+          (cons y2 y1)
+          (cons y1 y2)))))
+
 (define-public (note-head::calc-duration-log grob)
   (min 2
        (ly:duration-log