]> git.donarmstrong.com Git - lilypond.git/blobdiff - lily/tuplet-bracket.cc
Docs xref checking: append rather than prepend Documentation/ to search path
[lilypond.git] / lily / tuplet-bracket.cc
index b812e622e32b8d167bd649e5d2d1dcd9ca489230..0fd0150befa1cf370dadf8640a5a5fa6d604cddf 100644 (file)
@@ -1,10 +1,21 @@
 /*
 /*
-  tuplet-bracket.cc -- implement Tuplet_bracket
+  This file is part of LilyPond, the GNU music typesetter.
 
 
-  source file of the GNU LilyPond music typesetter
-
-  (c) 1997--2006 Jan Nieuwenhuizen <janneke@gnu.org>
+  Copyright (C) 1997--2009 Jan Nieuwenhuizen <janneke@gnu.org>
   Han-Wen Nienhuys <hanwen@xs4all.nl>
   Han-Wen Nienhuys <hanwen@xs4all.nl>
+
+  LilyPond is free software: you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation, either version 3 of the License, or
+  (at your option) any later version.
+
+  LilyPond is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with LilyPond.  If not, see <http://www.gnu.org/licenses/>.
 */
 
 /*
 */
 
 /*
@@ -62,20 +73,23 @@ get_x_bound_item (Grob *me_grob, Direction hdir, Direction my_dir)
 
 
 void
 
 
 void
-flatten_number_pair_property (Grob *me,
-                             Direction xdir,  SCM sym)
+flatten_number_pair_property (Grob *me, Direction xdir, SCM sym)
 {
   Drul_array<Real> zero (0, 0);
   Drul_array<Real> pair
     = robust_scm2drul (me->internal_get_property (sym), zero);
   pair[xdir] = 0.0;
 {
   Drul_array<Real> zero (0, 0);
   Drul_array<Real> pair
     = robust_scm2drul (me->internal_get_property (sym), zero);
   pair[xdir] = 0.0;
-  
-  me->internal_set_property (sym, ly_interval2scm (pair));
+
+  me->set_property (sym, ly_interval2scm (pair));
 }
 
 
 }
 
 
+/*
+  Return beam that encompasses the span of the tuplet bracket.
+*/
 Grob *
 Grob *
-Tuplet_bracket::parallel_beam (Grob *me_grob, vector<Grob*> const &cols, bool *equally_long)
+Tuplet_bracket::parallel_beam (Grob *me_grob, vector<Grob*> const &cols,
+                              bool *equally_long)
 {
   Spanner *me = dynamic_cast<Spanner *> (me_grob);
 
 {
   Spanner *me = dynamic_cast<Spanner *> (me_grob);
 
@@ -83,21 +97,23 @@ Tuplet_bracket::parallel_beam (Grob *me_grob, vector<Grob*> const &cols, bool *e
       || me->get_bound (RIGHT)->break_status_dir ())
     return 0;
 
       || me->get_bound (RIGHT)->break_status_dir ())
     return 0;
 
-  Drul_array<Grob*> stems (Note_column::get_stem (cols[0]),
+  Drul_array<Grob *> stems (Note_column::get_stem (cols[0]),
                           Note_column::get_stem (cols.back ()));
 
                           Note_column::get_stem (cols.back ()));
 
-  if (dynamic_cast<Item*> (stems[RIGHT])->get_column ()
-      != me->get_bound (RIGHT)->get_column())
+  if (!stems[RIGHT]
+      || !stems[LEFT]
+      || (dynamic_cast<Item*> (stems[RIGHT])->get_column ()
+         != me->get_bound (RIGHT)->get_column ()))
     return 0;
 
     return 0;
 
-  Drul_array<Grob*> beams;
+  Drul_array<Grob *> beams;
   Direction d = LEFT;
   do {
     beams[d] = stems[d] ? Stem::get_beam (stems[d]) : 0;
   } while (flip (&d) != LEFT);
   Direction d = LEFT;
   do {
     beams[d] = stems[d] ? Stem::get_beam (stems[d]) : 0;
   } while (flip (&d) != LEFT);
-  
+
   *equally_long = false;
   *equally_long = false;
-  if (! (beams[LEFT] && (beams[LEFT] == beams[RIGHT]) && !me->is_broken ()))
+  if (!(beams[LEFT] && (beams[LEFT] == beams[RIGHT]) && !me->is_broken ()))
     return 0;
 
   extract_grob_set (beams[LEFT], "stems", beam_stems);
     return 0;
 
   extract_grob_set (beams[LEFT], "stems", beam_stems);
@@ -109,12 +125,13 @@ Tuplet_bracket::parallel_beam (Grob *me_grob, vector<Grob*> const &cols, bool *e
     }
 
   *equally_long =
     }
 
   *equally_long =
-    (beam_stems[0] == stems[LEFT] && beam_stems.back () == stems[RIGHT]);
+    (beam_stems[0] == stems[LEFT]
+     && beam_stems.back () == stems[RIGHT]);
   return beams[LEFT];
 }
 
 
   return beams[LEFT];
 }
 
 
-MAKE_SCHEME_CALLBACK(Tuplet_bracket,calc_connect_to_neighbors,1);
+MAKE_SCHEME_CALLBACK (Tuplet_bracket, calc_connect_to_neighbors, 1);
 SCM
 Tuplet_bracket::calc_connect_to_neighbors (SCM smob)
 {
 SCM
 Tuplet_bracket::calc_connect_to_neighbors (SCM smob)
 {
@@ -129,9 +146,7 @@ Tuplet_bracket::calc_connect_to_neighbors (SCM smob)
   do
     {
       Direction break_dir = bounds[d]->break_status_dir ();
   do
     {
       Direction break_dir = bounds[d]->break_status_dir ();
-      
-      Spanner *orig_spanner = dynamic_cast<Spanner*> (me->original ());
-
+      Spanner *orig_spanner = dynamic_cast<Spanner *> (me->original ());
       vsize neighbor_idx = me->get_break_index () - break_dir;
       if (break_dir
          && d == RIGHT
       vsize neighbor_idx = me->get_break_index () - break_dir;
       if (break_dir
          && d == RIGHT
@@ -158,19 +173,19 @@ Tuplet_bracket::calc_connect_to_neighbors (SCM smob)
   return SCM_EOL;
 }
 
   return SCM_EOL;
 }
 
-Grob* 
+Grob 
 Tuplet_bracket::get_common_x (Spanner *me)
 {
   extract_grob_set (me, "note-columns", columns);
 
 Tuplet_bracket::get_common_x (Spanner *me)
 {
   extract_grob_set (me, "note-columns", columns);
 
-  Grob * commonx = common_refpoint_of_array (columns, me, X_AXIS);
+  Grob *commonx = common_refpoint_of_array (columns, me, X_AXIS);
   commonx = commonx->common_refpoint (me->get_bound (LEFT), X_AXIS);
   commonx = commonx->common_refpoint (me->get_bound (RIGHT), X_AXIS);
 
   return commonx;
 }
   commonx = commonx->common_refpoint (me->get_bound (LEFT), X_AXIS);
   commonx = commonx->common_refpoint (me->get_bound (RIGHT), X_AXIS);
 
   return commonx;
 }
-  
-MAKE_SCHEME_CALLBACK(Tuplet_bracket,calc_control_points,1)
+
+MAKE_SCHEME_CALLBACK (Tuplet_bracket, calc_control_points, 1)
 SCM
 Tuplet_bracket::calc_control_points (SCM smob)
 {
 SCM
 Tuplet_bracket::calc_control_points (SCM smob)
 {
@@ -178,8 +193,15 @@ Tuplet_bracket::calc_control_points (SCM smob)
 
   extract_grob_set (me, "note-columns", columns);
 
 
   extract_grob_set (me, "note-columns", columns);
 
+  SCM scm_positions = me->get_property ("positions");
+  if (!me->is_live ())
+    return SCM_EOL;
+
+  if (!scm_is_pair (scm_positions))
+    programming_error ("Positions should be number pair");
+
   Drul_array<Real> positions
   Drul_array<Real> positions
-    = ly_scm2realdrul (me->get_property ("positions"));
+    = robust_scm2drul (scm_positions, Drul_array<Real> (0, 0));
 
   Grob *commonx = get_common_x (me);
   Direction dir = get_grob_direction (me);
 
   Grob *commonx = get_common_x (me);
   Direction dir = get_grob_direction (me);
@@ -191,8 +213,7 @@ Tuplet_bracket::calc_control_points (SCM smob)
   Drul_array<bool> connect_to_other =
     robust_scm2booldrul (me->get_property ("connect-to-neighbor"),
                         Drul_array<bool> (false, false));
   Drul_array<bool> connect_to_other =
     robust_scm2booldrul (me->get_property ("connect-to-neighbor"),
                         Drul_array<bool> (false, false));
-  
-    
+
   Interval x_span;
   Direction d = LEFT;
   do
   Interval x_span;
   Direction d = LEFT;
   do
@@ -207,10 +228,11 @@ Tuplet_bracket::calc_control_points (SCM smob)
          if (d == RIGHT)
            x_span[d] += d * overshoot[d];
          else
          if (d == RIGHT)
            x_span[d] += d * overshoot[d];
          else
-           x_span[d] = robust_relative_extent (bounds[d], commonx, X_AXIS)[RIGHT]
+           x_span[d] = robust_relative_extent (bounds[d],
+                                               commonx, X_AXIS)[RIGHT]
              - overshoot[LEFT];
        }
              - overshoot[LEFT];
        }
-      
+
       else if (d == RIGHT
               && (columns.empty ()
                   || (bounds[d]->get_column ()
       else if (d == RIGHT
               && (columns.empty ()
                   || (bounds[d]->get_column ()
@@ -219,23 +241,25 @@ Tuplet_bracket::calc_control_points (SCM smob)
          /*
            We're connecting to a column, for the last bit of a broken
            fullLength bracket.
          /*
            We're connecting to a column, for the last bit of a broken
            fullLength bracket.
-           
-           TODO: make padding tunable?
          */
          */
-         Real padding = 1.0;
+         Real padding =
+           robust_scm2double(me->get_property("full-length-padding"), 1.0);
 
          if (bounds[d]->break_status_dir ())
            padding = 0.0;
 
          if (bounds[d]->break_status_dir ())
            padding = 0.0;
-         
-         x_span[d]
-           = robust_relative_extent (bounds[d], commonx, X_AXIS) [LEFT]
-           - padding;
+
+         Real coord = bounds[d]->relative_coordinate(commonx, X_AXIS);
+         if (to_boolean (me->get_property ("full-length-to-extent")))
+           coord = robust_relative_extent(bounds[d], commonx, X_AXIS)[LEFT];
+
+         coord = max (coord, x_span[LEFT]);
+
+         x_span[d] = coord - padding;
        }
     }
   while (flip (&d) != LEFT);
 
        }
     }
   while (flip (&d) != LEFT);
 
-  
-  
+
   x_span -= me->get_bound (LEFT)->relative_coordinate (commonx, X_AXIS);
   return scm_list_2 (ly_offset2scm (Offset (x_span[LEFT], positions[LEFT])),
                     ly_offset2scm (Offset (x_span[RIGHT], positions[RIGHT])));
   x_span -= me->get_bound (LEFT)->relative_coordinate (commonx, X_AXIS);
   return scm_list_2 (ly_offset2scm (Offset (x_span[LEFT], positions[LEFT])),
                     ly_offset2scm (Offset (x_span[RIGHT], positions[RIGHT])));
@@ -257,7 +281,7 @@ Tuplet_bracket::print (SCM smob)
   extract_grob_set (me, "note-columns", columns);
   bool equally_long = false;
   Grob *par_beam = parallel_beam (me, columns, &equally_long);
   extract_grob_set (me, "note-columns", columns);
   bool equally_long = false;
   Grob *par_beam = parallel_beam (me, columns, &equally_long);
-  
+
   bool bracket_visibility = !(par_beam && equally_long);
   /*
     Fixme: the type of this prop is sucky.
   bool bracket_visibility = !(par_beam && equally_long);
   /*
     Fixme: the type of this prop is sucky.
@@ -267,28 +291,36 @@ Tuplet_bracket::print (SCM smob)
     bracket_visibility = ly_scm2bool (bracket);
   else if (bracket == ly_symbol2scm ("if-no-beam"))
     bracket_visibility = !par_beam;
     bracket_visibility = ly_scm2bool (bracket);
   else if (bracket == ly_symbol2scm ("if-no-beam"))
     bracket_visibility = !par_beam;
-
   
   
-  SCM cpoints =  me->get_property ("control-points");
+  /*
+    Don't print a tuplet bracket and number if
+    no control-points were calculated
+  */
+  SCM cpoints = me->get_property ("control-points");
   if (scm_ilength (cpoints) < 2)
     {
       me->suicide ();
       return SCM_EOL;
     }
   if (scm_ilength (cpoints) < 2)
     {
       me->suicide ();
       return SCM_EOL;
     }
-  
+  /*  if the tuplet does not span any time, i.e. a single-note tuplet, hide
+      the bracket, but still let the number be displayed */
+  if (robust_scm2moment (me->get_bound (LEFT)->get_column ()->get_property ("when"), Moment (0))
+      == robust_scm2moment (me->get_bound (RIGHT)->get_column ()->get_property ("when"), Moment (0)))
+  {
+      bracket_visibility = false;
+  }
+
   Drul_array<Offset> points;
   points[LEFT] = ly_scm2offset (scm_car (cpoints));
   points[RIGHT] = ly_scm2offset (scm_cadr (cpoints));
   Drul_array<Offset> points;
   points[LEFT] = ly_scm2offset (scm_car (cpoints));
   points[RIGHT] = ly_scm2offset (scm_cadr (cpoints));
-  
+
   Interval x_span (points[LEFT][X_AXIS], points[RIGHT][X_AXIS]);
   Drul_array<Real> positions (points[LEFT][Y_AXIS], points[RIGHT][Y_AXIS]);
 
   Interval x_span (points[LEFT][X_AXIS], points[RIGHT][X_AXIS]);
   Drul_array<Real> positions (points[LEFT][Y_AXIS], points[RIGHT][Y_AXIS]);
 
-
-
   Output_def *pap = me->layout ();
 
   Grob *number_grob = unsmob_grob (me->get_object ("tuplet-number"));
   Output_def *pap = me->layout ();
 
   Grob *number_grob = unsmob_grob (me->get_object ("tuplet-number"));
-  
+
   /*
     No bracket when it would be smaller than the number.
   */
   /*
     No bracket when it would be smaller than the number.
   */
@@ -299,7 +331,7 @@ Tuplet_bracket::print (SCM smob)
       if (!ext.is_empty ())
        {
          gap = ext.length () + 1.0;
       if (!ext.is_empty ())
        {
          gap = ext.length () + 1.0;
-      
+
          if (0.75 * x_span.length () < gap)
            bracket_visibility = false;
        }
          if (0.75 * x_span.length () < gap)
            bracket_visibility = false;
        }
@@ -318,7 +350,7 @@ Tuplet_bracket::print (SCM smob)
       Drul_array<Stencil> edge_stencils;
 
       Direction dir = get_grob_direction (me);
       Drul_array<Stencil> edge_stencils;
 
       Direction dir = get_grob_direction (me);
-      
+
       scale_drul (&height, -ss * dir);
       scale_drul (&flare, ss);
       scale_drul (&shorten, ss);
       scale_drul (&height, -ss * dir);
       scale_drul (&flare, ss);
       scale_drul (&shorten, ss);
@@ -344,11 +376,13 @@ Tuplet_bracket::print (SCM smob)
                  SCM text = index_get_cell (edge_text, d);
                  if (Text_interface::is_markup (text))
                    {
                  SCM text = index_get_cell (edge_text, d);
                  if (Text_interface::is_markup (text))
                    {
-                     SCM t = Text_interface::interpret_markup (pap->self_scm (),
-                                                               properties, text);
+                     SCM t
+                       = Text_interface::interpret_markup (pap->self_scm (),
+                                                           properties, text);
 
                      Stencil *edge_text = unsmob_stencil (t);
 
                      Stencil *edge_text = unsmob_stencil (t);
-                     edge_text->translate_axis (x_span[d] - x_span[LEFT], X_AXIS);
+                     edge_text->translate_axis (x_span[d] - x_span[LEFT],
+                                                X_AXIS);
                      edge_stencils[d] = *edge_text;
                    }
                }
                      edge_stencils[d] = *edge_text;
                    }
                }
@@ -388,7 +422,7 @@ Tuplet_bracket::print (SCM smob)
 */
 Stencil
 Tuplet_bracket::make_bracket (Grob *me, // for line properties.
 */
 Stencil
 Tuplet_bracket::make_bracket (Grob *me, // for line properties.
-                             Axis protusion_axis,
+                             Axis protrusion_axis,
                              Offset dz,
                              Drul_array<Real> height,
                              Interval gap,
                              Offset dz,
                              Drul_array<Real> height,
                              Interval gap,
@@ -400,28 +434,27 @@ Tuplet_bracket::make_bracket (Grob *me, // for line properties.
   Real length = dz.length ();
   Drul_array<Offset> gap_corners;
 
   Real length = dz.length ();
   Drul_array<Offset> gap_corners;
 
-  Axis bracket_axis = other_axis (protusion_axis);
+  Axis bracket_axis = other_axis (protrusion_axis);
 
   Drul_array<Offset> straight_corners = corners;
 
   Direction d = LEFT;
   do
     straight_corners[d] += -d * shorten[d] / length * dz;
 
   Drul_array<Offset> straight_corners = corners;
 
   Direction d = LEFT;
   do
     straight_corners[d] += -d * shorten[d] / length * dz;
-  while (flip (&d) != LEFT)
-    ;
+  while (flip (&d) != LEFT);
 
 
-  if (gap.is_empty ())
-    gap = Interval (0, 0);
-  do
-    gap_corners[d] = (dz * 0.5) + gap[d] / length * dz;
-  while (flip (&d) != LEFT)
-    ;
+  if (!gap.is_empty ())
+    {
+      do
+       gap_corners[d] = (dz * 0.5) + gap[d] / length * dz;
+      while (flip (&d) != LEFT);
+    }
 
   Drul_array<Offset> flare_corners = straight_corners;
   do
     {
       flare_corners[d][bracket_axis] = straight_corners[d][bracket_axis];
 
   Drul_array<Offset> flare_corners = straight_corners;
   do
     {
       flare_corners[d][bracket_axis] = straight_corners[d][bracket_axis];
-      flare_corners[d][protusion_axis] += height[d];
+      flare_corners[d][protrusion_axis] += height[d];
       straight_corners[d][bracket_axis] += -d * flare[d];
     }
   while (flip (&d) != LEFT);
       straight_corners[d][bracket_axis] += -d * flare[d];
     }
   while (flip (&d) != LEFT);
@@ -429,14 +462,20 @@ Tuplet_bracket::make_bracket (Grob *me, // for line properties.
   Stencil m;
   do
     {
   Stencil m;
   do
     {
-      m.add_stencil (Line_interface::line (me, straight_corners[d],
-                                          gap_corners[d]));
+      if (!gap.is_empty ())
+       m.add_stencil (Line_interface::line (me, straight_corners[d],
+                                            gap_corners[d]));
 
       m.add_stencil (Line_interface::line (me, straight_corners[d],
                                           flare_corners[d]));
     }
 
       m.add_stencil (Line_interface::line (me, straight_corners[d],
                                           flare_corners[d]));
     }
+
   while (flip (&d) != LEFT);
 
   while (flip (&d) != LEFT);
 
+  if (gap.is_empty ())
+    m.add_stencil (Line_interface::line (me, straight_corners[LEFT],
+                                        straight_corners[RIGHT]));
+
   return m;
 }
 
   return m;
 }
 
@@ -476,73 +515,113 @@ Tuplet_bracket::calc_position_and_height (Grob *me_grob, Real *offset, Real *dy)
   commony = common_refpoint_of_array (tuplets, commony, Y_AXIS);
   if (Grob *st = Staff_symbol_referencer::get_staff_symbol (me))
     commony = st->common_refpoint (commony, Y_AXIS);
   commony = common_refpoint_of_array (tuplets, commony, Y_AXIS);
   if (Grob *st = Staff_symbol_referencer::get_staff_symbol (me))
     commony = st->common_refpoint (commony, Y_AXIS);
+  Real my_offset = me->relative_coordinate (commony, Y_AXIS);
 
   Grob *commonx = get_common_x (me);
   commonx = common_refpoint_of_array (tuplets, commonx, Y_AXIS);
 
   Interval staff;
 
   Grob *commonx = get_common_x (me);
   commonx = common_refpoint_of_array (tuplets, commonx, Y_AXIS);
 
   Interval staff;
-  if (Grob *st = Staff_symbol_referencer::get_staff_symbol (me))
+  Grob *st = Staff_symbol_referencer::get_staff_symbol (me);
+
+  /* staff-padding doesn't work correctly on cross-staff tuplets
+     because it only considers one staff symbol. Until this works,
+     disable it. */
+  if (st && !to_boolean (me->get_property ("cross-staff")))
     {
       Real pad = robust_scm2double (me->get_property ("staff-padding"), -1.0);
       if  (pad >= 0.0)
        {
     {
       Real pad = robust_scm2double (me->get_property ("staff-padding"), -1.0);
       if  (pad >= 0.0)
        {
-         staff = st->extent (commony, Y_AXIS);
+         staff = st->extent (commony, Y_AXIS) - my_offset;
          staff.widen (pad);
        }
     }
          staff.widen (pad);
        }
     }
-  
-  Direction dir = get_grob_direction (me);
 
 
-  /*
-    Use outer non-rest columns to determine slope
-  */
-  Grob *left_col = 0;
-  Grob *right_col = 0;
-  get_bounds (me, &left_col, &right_col);
-  if (left_col && right_col)
-    {
-      Interval rv = right_col->extent (commony, Y_AXIS);
-      Interval lv = left_col->extent (commony, Y_AXIS);
-      rv.unite (staff);
-      lv.unite (staff);
-      Real graphical_dy = rv[dir] - lv[dir];
-
-      Slice ls = Note_column::head_positions_interval (left_col);
-      Slice rs = Note_column::head_positions_interval (right_col);
-
-      Interval musical_dy;
-      musical_dy[UP] = rs[UP] - ls[UP];
-      musical_dy[DOWN] = rs[DOWN] - ls[DOWN];
-      if (sign (musical_dy[UP]) != sign (musical_dy[DOWN]))
-       *dy = 0.0;
-      else if (sign (graphical_dy) != sign (musical_dy[DOWN]))
-       *dy = 0.0;
-      else
-       *dy = graphical_dy;
-    }
-  else
-    *dy = 0;
+  Direction dir = get_grob_direction (me);
 
 
-  *offset = -dir * infinity_f;
+  bool equally_long = false;
+  Grob *par_beam = parallel_beam (me, columns, &equally_long);
 
   Item *lgr = get_x_bound_item (me, LEFT, dir);
   Item *rgr = get_x_bound_item (me, RIGHT, dir);
   Real x0 = robust_relative_extent (lgr, commonx, X_AXIS)[LEFT];
   Real x1 = robust_relative_extent (rgr, commonx, X_AXIS)[RIGHT];
 
   Item *lgr = get_x_bound_item (me, LEFT, dir);
   Item *rgr = get_x_bound_item (me, RIGHT, dir);
   Real x0 = robust_relative_extent (lgr, commonx, X_AXIS)[LEFT];
   Real x1 = robust_relative_extent (rgr, commonx, X_AXIS)[RIGHT];
+  bool follow_beam = par_beam
+    && get_grob_direction (par_beam) == dir
+    && ! to_boolean (par_beam->get_property ("knee"));
 
   vector<Offset> points;
 
   vector<Offset> points;
-  points.push_back (Offset (x0 - x0, staff[dir]));
-  points.push_back (Offset (x1 - x0, staff[dir]));
-
-  for (vsize i = 0; i < columns.size (); i++)
+  if (columns.size ()
+      && follow_beam
+      && 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)
+        + 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)
+        + stems[RIGHT]->get_parent (Y_AXIS)->relative_coordinate (commony, Y_AXIS);
+
+      *dy = rp - lp;
+      points.push_back (Offset (stems[LEFT]->relative_coordinate (commonx, X_AXIS) - x0, lp));
+      points.push_back (Offset (stems[RIGHT]->relative_coordinate (commonx, X_AXIS) - x0, rp));
+    }
+  else
     {
     {
-      Interval note_ext = columns[i]->extent (commony, Y_AXIS);
-      Real notey = note_ext[dir] - me->relative_coordinate (commony, Y_AXIS);
+      /*
+       Use outer non-rest columns to determine slope
+      */
+      Grob *left_col = 0;
+      Grob *right_col = 0;
+      get_bounds (me, &left_col, &right_col);
+      if (left_col && right_col)
+       {
+         Interval rv = Note_column::cross_staff_extent (right_col, commony);
+         Interval lv = Note_column::cross_staff_extent (left_col, commony);
+         rv.unite (staff);
+         lv.unite (staff);
+
+         Real graphical_dy = rv[dir] - lv[dir];
+
+         Slice ls = Note_column::head_positions_interval (left_col);
+         Slice rs = Note_column::head_positions_interval (right_col);
+
+         Interval musical_dy;
+         musical_dy[UP] = rs[UP] - ls[UP];
+         musical_dy[DOWN] = rs[DOWN] - ls[DOWN];
+         if (sign (musical_dy[UP]) != sign (musical_dy[DOWN]))
+           *dy = 0.0;
+         else if (sign (graphical_dy) != sign (musical_dy[DOWN]))
+           *dy = 0.0;
+         else
+           *dy = graphical_dy;
+       }
+      else
+       *dy = 0;
 
 
-      Real x = columns[i]->relative_coordinate (commonx, X_AXIS) - x0;
-      points.push_back (Offset (x, notey));
+      for (vsize i = 0; i < columns.size (); i++)
+       {
+         Interval note_ext = Note_column::cross_staff_extent (columns[i],
+                                                              commony);
+         Real x = columns[i]->relative_coordinate (commonx, X_AXIS) - x0;
+
+         points.push_back (Offset (x, note_ext[dir]));
+       }
     }
 
     }
 
+  if (!follow_beam)
+    {
+      points.push_back (Offset (x0 - x0, staff[dir]));
+      points.push_back (Offset (x1 - x0, staff[dir]));
+    }
+  
   /*
     This is a slight hack. We compute two encompass points from the
     bbox of the smaller tuplets.
   /*
     This is a slight hack. We compute two encompass points from the
     bbox of the smaller tuplets.
@@ -555,10 +634,14 @@ Tuplet_bracket::calc_position_and_height (Grob *me_grob, Real *offset, Real *dy)
       Interval tuplet_x (tuplets[i]->extent (commonx, X_AXIS));
       Interval tuplet_y (tuplets[i]->extent (commony, Y_AXIS));
 
       Interval tuplet_x (tuplets[i]->extent (commonx, X_AXIS));
       Interval tuplet_y (tuplets[i]->extent (commony, Y_AXIS));
 
+      if (!tuplets[i]->is_live ())
+       continue;
+
       Direction d = LEFT;
       Direction d = LEFT;
-      Drul_array<Real> positions = ly_scm2realdrul (tuplets[i]->get_property ("positions"));
+      Drul_array<Real> positions
+       = robust_scm2interval (tuplets[i]->get_property ("positions"),
+                              Interval (0,0));
 
 
-      
       Real other_dy = positions[RIGHT] - positions[LEFT];
 
       do
       Real other_dy = positions[RIGHT] - positions[LEFT];
 
       do
@@ -577,11 +660,12 @@ Tuplet_bracket::calc_position_and_height (Grob *me_grob, Real *offset, Real *dy)
       while (flip (&d) != LEFT);
     }
 
       while (flip (&d) != LEFT);
     }
 
+  *offset = -dir * infinity_f;
   Real factor = (columns.size () > 1) ? 1 / (x1 - x0) : 1.0;
   for (vsize i = 0; i < points.size (); i++)
     {
       Real x = points[i][X_AXIS];
   Real factor = (columns.size () > 1) ? 1 / (x1 - x0) : 1.0;
   for (vsize i = 0; i < points.size (); i++)
     {
       Real x = points[i][X_AXIS];
-      Real tuplety = (*dy) * x * factor;
+      Real tuplety = (*dy) * x * factor + my_offset;
 
       if (points[i][Y_AXIS] * dir > (*offset + tuplety) * dir)
        *offset = points[i][Y_AXIS] - tuplety;
 
       if (points[i][Y_AXIS] * dir > (*offset + tuplety) * dir)
        *offset = points[i][Y_AXIS] - tuplety;
@@ -595,6 +679,8 @@ Tuplet_bracket::calc_position_and_height (Grob *me_grob, Real *offset, Real *dy)
     Kind of pointless since we put them outside the staff anyway, but
     let's leave code for the future when possibly allow them to move
     into the staff once again.
     Kind of pointless since we put them outside the staff anyway, but
     let's leave code for the future when possibly allow them to move
     into the staff once again.
+  
+    This doesn't seem to support cross-staff tuplets atm.
   */
   if (*dy == 0
       && fabs (*offset) < ss * Staff_symbol_referencer::staff_radius (me))
   */
   if (*dy == 0
       && fabs (*offset) < ss * Staff_symbol_referencer::staff_radius (me))
@@ -610,7 +696,6 @@ Tuplet_bracket::calc_position_and_height (Grob *me_grob, Real *offset, Real *dy)
     }
 }
 
     }
 }
 
-
 MAKE_SCHEME_CALLBACK (Tuplet_bracket, calc_direction, 1);
 SCM
 Tuplet_bracket::calc_direction (SCM smob)
 MAKE_SCHEME_CALLBACK (Tuplet_bracket, calc_direction, 1);
 SCM
 Tuplet_bracket::calc_direction (SCM smob)
@@ -625,52 +710,14 @@ SCM
 Tuplet_bracket::calc_positions (SCM smob)
 {
   Spanner *me = unsmob_spanner (smob);
 Tuplet_bracket::calc_positions (SCM smob)
 {
   Spanner *me = unsmob_spanner (smob);
-  extract_grob_set (me, "note-columns", columns);
-
-
-  /*
-    Don't print if it doesn't span time.
-   */
-  if (robust_scm2moment (me->get_bound (LEFT)->get_column ()->get_property ("when"), Moment (0))
-      == robust_scm2moment (me->get_bound (RIGHT)->get_column ()->get_property ("when"), Moment (0)))
-    {
-      me->suicide ();
-      return SCM_EOL;
-    }
-
-  
-  Direction dir = get_grob_direction (me);
-  bool equally_long = false;
-  Grob *par_beam = parallel_beam (me, columns, &equally_long);
 
 
-  /*
-    We follow the beam only if there is one, and we are next to it.
-  */
   Real dy = 0.0;
   Real offset = 0.0;
   Real dy = 0.0;
   Real offset = 0.0;
-  if (!par_beam
-      || get_grob_direction (par_beam) != dir)
-    calc_position_and_height (me, &offset, &dy);
-  else
-    {
-      SCM ps = par_beam->get_property ("positions");
+  calc_position_and_height (me, &offset, &dy);
 
 
-      Real lp = scm_to_double (scm_car (ps));
-      Real rp = scm_to_double (scm_cdr (ps));
-
-      Real ss = Staff_symbol_referencer::staff_space (me);
-      
-      offset = lp + dir * (0.5 + scm_to_double (me->get_property ("padding")));
-      dy = (rp - lp);
-
-      dy *= ss;
-      offset *= ss;    
-    }
-
-  
   SCM x = scm_cons (scm_from_double (offset),
                    scm_from_double (offset + dy));
   SCM x = scm_cons (scm_from_double (offset),
                    scm_from_double (offset + dy));
-  
+
   return x;
 }
 
   return x;
 }
 
@@ -706,15 +753,43 @@ Tuplet_bracket::add_tuplet_bracket (Grob *me, Grob *bracket)
   Pointer_group_interface::add_grob (me, ly_symbol2scm ("tuplets"), bracket);
 }
 
   Pointer_group_interface::add_grob (me, ly_symbol2scm ("tuplets"), bracket);
 }
 
-ADD_INTERFACE (Tuplet_bracket,
-              "tuplet-bracket-interface",
-              "A bracket with a number in the middle, used for tuplets. "
-              "When the bracket spans  a line break, the value of "
-              "@code{break-overshoot} determines how far it extends "
-              "beyond the staff. "
-              "At a line break, the markups in the @code{edge-text} are printed "
-              "at the edges. ",
+MAKE_SCHEME_CALLBACK (Tuplet_bracket, calc_cross_staff, 1);
+SCM
+Tuplet_bracket::calc_cross_staff (SCM smob)
+{
+  Grob *me = unsmob_grob (smob);
+  extract_grob_set (me, "note-columns", cols);
+  extract_grob_set (me, "tuplets", tuplets);
+
+  Grob *commony = common_refpoint_of_array (cols, me, Y_AXIS);
+  commony = common_refpoint_of_array (tuplets, commony, Y_AXIS);
+  if (Grob *st = Staff_symbol_referencer::get_staff_symbol (me))
+    commony = st->common_refpoint (commony, Y_AXIS);
+  if (me->check_cross_staff (commony))
+    return SCM_BOOL_T;
+
+  bool equally_long = false;
+  Grob *par_beam = parallel_beam (me, cols, &equally_long);
+
+  if (par_beam && to_boolean (par_beam->get_property ("cross-staff")))
+    return SCM_BOOL_T;
 
 
+  for (vsize i = 0; i < cols.size (); i++)
+    {
+      Grob *stem = unsmob_grob (cols[i]->get_object ("stem"));
+      if (stem && to_boolean (stem->get_property ("cross-staff")))
+       return SCM_BOOL_T;
+    }
+
+  return SCM_BOOL_F;
+}
+
+ADD_INTERFACE (Tuplet_bracket,
+              "A bracket with a number in the middle, used for tuplets."
+              "  When the bracket spans a line break, the value of"
+              " @code{break-overshoot} determines how far it extends"
+              " beyond the staff.  At a line break, the markups in the"
+              " @code{edge-text} are printed at the edges.",
 
               /* properties */
               "bracket-flare "
 
               /* properties */
               "bracket-flare "
@@ -725,6 +800,8 @@ ADD_INTERFACE (Tuplet_bracket,
               "direction "
               "edge-height "
               "edge-text "
               "direction "
               "edge-height "
               "edge-text "
+              "full-length-padding "
+              "full-length-to-extent "
               "gap "
               "positions "
               "note-columns "
               "gap "
               "positions "
               "note-columns "
@@ -733,6 +810,5 @@ ADD_INTERFACE (Tuplet_bracket,
               "shorten-pair "
               "staff-padding "
               "thickness "
               "shorten-pair "
               "staff-padding "
               "thickness "
-              "tuplets ");
-
-
+              "tuplets "
+              );