]> git.donarmstrong.com Git - lilypond.git/blobdiff - lily/tuplet-bracket.cc
[lilypond] parse-scm.o depends on parser.hh.
[lilypond.git] / lily / tuplet-bracket.cc
index dde38da22d9fc984a029a6c6b48ca81a5a79207a..c682b67b48fe99954c19999442dd9aa38a76980b 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--2007 Jan Nieuwenhuizen <janneke@gnu.org>
+  Copyright (C) 1997--2011 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/>.
 */
 
 /*
 */
 
 /*
@@ -29,7 +40,6 @@
   todo: handle breaking elegantly.
 */
 
   todo: handle breaking elegantly.
 */
 
-
 #include "tuplet-bracket.hh"
 #include "line-interface.hh"
 #include "beam.hh"
 #include "tuplet-bracket.hh"
 #include "line-interface.hh"
 #include "beam.hh"
@@ -41,6 +51,7 @@
 #include "note-column.hh"
 #include "pointer-group-interface.hh"
 #include "directional-element-interface.hh"
 #include "note-column.hh"
 #include "pointer-group-interface.hh"
 #include "directional-element-interface.hh"
+#include "skyline.hh"
 #include "spanner.hh"
 #include "staff-symbol-referencer.hh"
 #include "lookup.hh"
 #include "spanner.hh"
 #include "staff-symbol-referencer.hh"
 #include "lookup.hh"
@@ -60,26 +71,23 @@ get_x_bound_item (Grob *me_grob, Direction hdir, Direction my_dir)
   return g;
 }
 
   return g;
 }
 
-
 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->set_property (sym, ly_interval2scm (pair));
 }
 
   me->set_property (sym, ly_interval2scm (pair));
 }
 
-
 /*
   Return beam that encompasses the span of the tuplet bracket.
 */
 Grob *
 /*
   Return beam that encompasses the span of the tuplet bracket.
 */
 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);
 
@@ -87,21 +95,21 @@ Tuplet_bracket::parallel_beam (Grob *me_grob, vector<Grob*> const &cols,
       || 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]),
-                          Note_column::get_stem (cols.back ()));
+  Drul_array<Grob *> stems (Note_column::get_stem (cols[0]),
+                            Note_column::get_stem (cols.back ()));
 
   if (!stems[RIGHT]
       || !stems[LEFT]
 
   if (!stems[RIGHT]
       || !stems[LEFT]
-      || (dynamic_cast<Item*> (stems[RIGHT])->get_column ()
-         != me->get_bound (RIGHT)->get_column ()))
+      || (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;
   Direction d = LEFT;
-  do {
+  do
     beams[d] = stems[d] ? Stem::get_beam (stems[d]) : 0;
     beams[d] = stems[d] ? Stem::get_beam (stems[d]) : 0;
-  while (flip (&d) != LEFT);
-  
+  while (flip (&d) != LEFT);
+
   *equally_long = false;
   if (! (beams[LEFT] && (beams[LEFT] == beams[RIGHT]) && !me->is_broken ()))
     return 0;
   *equally_long = false;
   if (! (beams[LEFT] && (beams[LEFT] == beams[RIGHT]) && !me->is_broken ()))
     return 0;
@@ -114,85 +122,72 @@ Tuplet_bracket::parallel_beam (Grob *me_grob, vector<Grob*> const &cols,
       return 0;
     }
 
       return 0;
     }
 
-  *equally_long =
-    (beam_stems[0] == stems[LEFT]
-     && beam_stems.back () == stems[RIGHT]);
+  *equally_long
+    (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)
 {
   Spanner *me = unsmob_spanner (smob);
 
 SCM
 Tuplet_bracket::calc_connect_to_neighbors (SCM smob)
 {
   Spanner *me = unsmob_spanner (smob);
 
-  Direction dir = get_grob_direction (me); 
+  Direction dir = get_grob_direction (me);
   Drul_array<Item *> bounds (get_x_bound_item (me, LEFT, dir),
   Drul_array<Item *> bounds (get_x_bound_item (me, LEFT, dir),
-                            get_x_bound_item (me, RIGHT, dir));
-  
+                             get_x_bound_item (me, RIGHT, dir));
+
   Drul_array<bool> connect_to_other (false, false);
   Direction d = LEFT;
   do
     {
       Direction break_dir = bounds[d]->break_status_dir ();
   Drul_array<bool> connect_to_other (false, false);
   Direction d = LEFT;
   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
       vsize neighbor_idx = me->get_break_index () - break_dir;
       if (break_dir
-         && d == RIGHT
-         && neighbor_idx < orig_spanner->broken_intos_.size ())
-       {
-         Grob *neighbor = orig_spanner->broken_intos_[neighbor_idx];
+          && d == RIGHT
+          && neighbor_idx < orig_spanner->broken_intos_.size ())
+        {
+          Grob *neighbor = orig_spanner->broken_intos_[neighbor_idx];
 
 
-         /* trigger possible suicide*/
-         (void) neighbor->get_property ("positions");
-       }
+          /* trigger possible suicide*/
+          (void) neighbor->get_property ("positions");
+        }
 
       connect_to_other[d]
 
       connect_to_other[d]
-       = (break_dir
-          && neighbor_idx < orig_spanner->broken_intos_.size ()
-          && orig_spanner->broken_intos_[neighbor_idx]->is_live ());
+        = (break_dir
+           && neighbor_idx < orig_spanner->broken_intos_.size ()
+           && orig_spanner->broken_intos_[neighbor_idx]->is_live ());
     }
   while (flip (&d) != LEFT);
 
     }
   while (flip (&d) != LEFT);
 
-
   if (connect_to_other[LEFT] || connect_to_other[RIGHT])
     return scm_cons (scm_from_bool (connect_to_other[LEFT]),
   if (connect_to_other[LEFT] || connect_to_other[RIGHT])
     return scm_cons (scm_from_bool (connect_to_other[LEFT]),
-                    scm_from_bool (connect_to_other[RIGHT]));
-                    
+                     scm_from_bool (connect_to_other[RIGHT]));
+
   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_x_positions, 1)
 SCM
 SCM
-Tuplet_bracket::calc_control_points (SCM smob)
+Tuplet_bracket::calc_x_positions (SCM smob)
 {
   Spanner *me = unsmob_spanner (smob);
 {
   Spanner *me = unsmob_spanner (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
-    = 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);
 
@@ -200,11 +195,10 @@ Tuplet_bracket::calc_control_points (SCM smob)
   bounds[LEFT] = get_x_bound_item (me, LEFT, dir);
   bounds[RIGHT] = get_x_bound_item (me, RIGHT, dir);
 
   bounds[LEFT] = get_x_bound_item (me, LEFT, dir);
   bounds[RIGHT] = get_x_bound_item (me, RIGHT, dir);
 
-  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
@@ -212,45 +206,45 @@ Tuplet_bracket::calc_control_points (SCM smob)
       x_span[d] = robust_relative_extent (bounds[d], commonx, X_AXIS)[d];
 
       if (connect_to_other[d])
       x_span[d] = robust_relative_extent (bounds[d], commonx, X_AXIS)[d];
 
       if (connect_to_other[d])
-       {
-         Interval overshoot (robust_scm2drul (me->get_property ("break-overshoot"),
-                                              Interval (-0.5, 0.0)));
-
-         if (d == RIGHT)
-           x_span[d] += d * overshoot[d];
-         else
-           x_span[d] = robust_relative_extent (bounds[d], commonx, X_AXIS)[RIGHT]
-             - overshoot[LEFT];
-       }
-      
+        {
+          Interval overshoot (robust_scm2drul (me->get_property ("break-overshoot"),
+                                               Interval (-0.5, 0.0)));
+
+          if (d == RIGHT)
+            x_span[d] += d * overshoot[d];
+          else
+            x_span[d] = robust_relative_extent (bounds[d],
+                                                commonx, X_AXIS)[RIGHT]
+                        - overshoot[LEFT];
+        }
+
       else if (d == RIGHT
       else if (d == RIGHT
-              && (columns.empty ()
-                  || (bounds[d]->get_column ()
-                      != dynamic_cast<Item *> (columns.back ())->get_column ())))
-       {
-         /*
-           We're connecting to a column, for the last bit of a broken
-           fullLength bracket.
-           
-           TODO: make padding tunable?
-         */
-         Real padding = 1.0;
-
-         if (bounds[d]->break_status_dir ())
-           padding = 0.0;
-         
-         x_span[d]
-           = robust_relative_extent (bounds[d], commonx, X_AXIS) [LEFT]
-           - padding;
-       }
+               && (columns.empty ()
+                   || (bounds[d]->get_column ()
+                       != dynamic_cast<Item *> (columns.back ())->get_column ())))
+        {
+          /*
+            We're connecting to a column, for the last bit of a broken
+            fullLength bracket.
+          */
+          Real padding
+            = robust_scm2double (me->get_property ("full-length-padding"), 1.0);
+
+          if (bounds[d]->break_status_dir ())
+            padding = 0.0;
+
+          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])));
+  return ly_interval2scm (x_span - me->get_bound (LEFT)->relative_coordinate (commonx, X_AXIS));
 }
 
 /*
 }
 
 /*
@@ -269,57 +263,67 @@ 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);
+
+  bool bracket_visibility = !(par_beam && equally_long); // Flag, print/don't print tuplet bracket.
   /*
   /*
-    Fixme: the type of this prop is sucky.
+    FIXME: The type of this prop is sucky.
   */
   */
-  SCM bracket = me->get_property ("bracket-visibility");
-  if (scm_is_bool (bracket))
-    bracket_visibility = ly_scm2bool (bracket);
-  else if (bracket == ly_symbol2scm ("if-no-beam"))
+  SCM bracket_vis_prop = me->get_property ("bracket-visibility");
+  bool bracket_prop = ly_scm2bool (bracket_vis_prop); // Flag, user has set bracket-visibility prop.
+  bool bracket = (bracket_vis_prop == ly_symbol2scm ("if-no-beam"));
+  if (scm_is_bool (bracket_vis_prop))
+    bracket_visibility = bracket_prop;
+  else if (bracket)
     bracket_visibility = !par_beam;
     bracket_visibility = !par_beam;
-  
-  /* 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)
+
+  /*
+    Don't print a tuplet bracket and number if
+    no X or Y positions were calculated.
+  */
+  SCM scm_x_span = me->get_property ("X-positions");
+  SCM scm_positions = me->get_property ("positions");
+  if (!scm_is_pair (scm_x_span) || !scm_is_pair (scm_positions))
     {
       me->suicide ();
       return SCM_EOL;
     }
   /*  if the tuplet does not span any time, i.e. a single-note tuplet, hide
     {
       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;
-  }
-  
+      the bracket, but still let the number be displayed.
+      Only do this if the user has not explicitly specified bracket-visibility = #t.
+  */
+  if (!to_boolean (bracket_vis_prop)
+      && (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;
+
+  Interval x_span = robust_scm2interval (scm_x_span, Interval (0.0, 0.0));
+  Interval positions = robust_scm2interval (scm_positions, Interval (0.0, 0.0));
+
   Drul_array<Offset> points;
   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]);
+  Direction d = LEFT;
+  do
+    points[d] = Offset (x_span[d], positions[d]);
+  while (flip (&d) != LEFT);
 
   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.
+    Don't print the bracket when it would be smaller than the number.
+    ...Unless the user has coded bracket-visibility = #t, that is.
   */
   Real gap = 0.;
   if (bracket_visibility && number_grob)
     {
       Interval ext = number_grob->extent (number_grob, X_AXIS);
       if (!ext.is_empty ())
   */
   Real gap = 0.;
   if (bracket_visibility && number_grob)
     {
       Interval ext = number_grob->extent (number_grob, X_AXIS);
       if (!ext.is_empty ())
-       {
-         gap = ext.length () + 1.0;
-      
-         if (0.75 * x_span.length () < gap)
-           bracket_visibility = false;
-       }
+        {
+          gap = ext.length () + 1.0;
+
+          if ((0.75 * x_span.length () < gap) && !bracket_prop)
+            bracket_visibility = false;
+        }
     }
 
   if (bracket_visibility)
     }
 
   if (bracket_visibility)
@@ -327,67 +331,69 @@ Tuplet_bracket::print (SCM smob)
       Drul_array<Real> zero (0, 0);
       Real ss = Staff_symbol_referencer::staff_space (me);
       Drul_array<Real> height
       Drul_array<Real> zero (0, 0);
       Real ss = Staff_symbol_referencer::staff_space (me);
       Drul_array<Real> height
-       = robust_scm2drul (me->get_property ("edge-height"), zero);
+        = robust_scm2drul (me->get_property ("edge-height"), zero);
       Drul_array<Real> flare
       Drul_array<Real> flare
-       = robust_scm2drul (me->get_property ("bracket-flare"), zero);
+        = robust_scm2drul (me->get_property ("bracket-flare"), zero);
       Drul_array<Real> shorten
       Drul_array<Real> shorten
-       = robust_scm2drul (me->get_property ("shorten-pair"), zero);
+        = robust_scm2drul (me->get_property ("shorten-pair"), zero);
       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);
 
-      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));
 
       Direction d = LEFT;
       do
 
       Direction d = LEFT;
       do
-       {
-         if (connect_to_other[d])
-           {
-             height[d] = 0.0;
-             flare[d] = 0.0;
-             shorten[d] = 0.0;
-
-             SCM edge_text = me->get_property ("edge-text");
-
-             if (scm_is_pair (edge_text))
-               {
-                 SCM properties = Font_interface::text_font_alist_chain (me);
-                 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);
-
-                     Stencil *edge_text = unsmob_stencil (t);
-                     edge_text->translate_axis (x_span[d] - x_span[LEFT], X_AXIS);
-                     edge_stencils[d] = *edge_text;
-                   }
-               }
-           }
-       }
+        {
+          if (connect_to_other[d])
+            {
+              height[d] = 0.0;
+              flare[d] = 0.0;
+              shorten[d] = 0.0;
+
+              SCM edge_text = me->get_property ("edge-text");
+
+              if (scm_is_pair (edge_text))
+                {
+                  SCM properties = Font_interface::text_font_alist_chain (me);
+                  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);
+
+                      Stencil *edge_text = unsmob_stencil (t);
+                      edge_text->translate_axis (x_span[d] - x_span[LEFT],
+                                                 X_AXIS);
+                      edge_stencils[d] = *edge_text;
+                    }
+                }
+            }
+        }
       while (flip (&d) != LEFT);
 
       Stencil brack = make_bracket (me, Y_AXIS,
       while (flip (&d) != LEFT);
 
       Stencil brack = make_bracket (me, Y_AXIS,
-                                   points[RIGHT] - points[LEFT],
-                                   height,
-                                   /*
-                                     0.1 = more space at right due to italics
-                                     TODO: use italic correction of font.
-                                   */
-                                   Interval (-0.5, 0.5) * gap + 0.1,
-                                   flare, shorten);
+                                    points[RIGHT] - points[LEFT],
+                                    height,
+                                    /*
+                                      0.1 = more space at right due to italics
+                                      TODO: use italic correction of font.
+                                    */
+                                    Interval (-0.5, 0.5) * gap + 0.1,
+                                    flare, shorten);
 
       do
 
       do
-       {
-         if (!edge_stencils[d].is_empty ())
-           brack.add_stencil (edge_stencils[d]);
-       }
+        {
+          if (!edge_stencils[d].is_empty ())
+            brack.add_stencil (edge_stencils[d]);
+        }
       while (flip (&d) != LEFT);
 
       mol.add_stencil (brack);
       while (flip (&d) != LEFT);
 
       mol.add_stencil (brack);
@@ -405,19 +411,19 @@ 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,
-                             Offset dz,
-                             Drul_array<Real> height,
-                             Interval gap,
-                             Drul_array<Real> flare,
-                             Drul_array<Real> shorten)
+                              Axis protrusion_axis,
+                              Offset dz,
+                              Drul_array<Real> height,
+                              Interval gap,
+                              Drul_array<Real> flare,
+                              Drul_array<Real> shorten)
 {
   Drul_array<Offset> corners (Offset (0, 0), dz);
 
   Real length = dz.length ();
   Drul_array<Offset> gap_corners;
 
 {
   Drul_array<Offset> corners (Offset (0, 0), dz);
 
   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;
 
 
   Drul_array<Offset> straight_corners = corners;
 
@@ -429,7 +435,7 @@ Tuplet_bracket::make_bracket (Grob *me, // for line properties.
   if (!gap.is_empty ())
     {
       do
   if (!gap.is_empty ())
     {
       do
-       gap_corners[d] = (dz * 0.5) + gap[d] / length * dz;
+        gap_corners[d] = (dz * 0.5) + gap[d] / length * dz;
       while (flip (&d) != LEFT);
     }
 
       while (flip (&d) != LEFT);
     }
 
@@ -437,7 +443,7 @@ Tuplet_bracket::make_bracket (Grob *me, // for line properties.
   do
     {
       flare_corners[d][bracket_axis] = straight_corners[d][bracket_axis];
   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);
@@ -446,19 +452,19 @@ Tuplet_bracket::make_bracket (Grob *me, // for line properties.
   do
     {
       if (!gap.is_empty ())
   do
     {
       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],
+                                             gap_corners[d]));
 
       m.add_stencil (Line_interface::line (me, straight_corners[d],
 
       m.add_stencil (Line_interface::line (me, straight_corners[d],
-                                          flare_corners[d]));
+                                           flare_corners[d]));
     }
 
   while (flip (&d) != LEFT);
 
   if (gap.is_empty ())
     m.add_stencil (Line_interface::line (me, straight_corners[LEFT],
     }
 
   while (flip (&d) != LEFT);
 
   if (gap.is_empty ())
     m.add_stencil (Line_interface::line (me, straight_corners[LEFT],
-                                        straight_corners[RIGHT]));
-  
+                                         straight_corners[RIGHT]));
+
   return m;
 }
 
   return m;
 }
 
@@ -471,7 +477,7 @@ Tuplet_bracket::get_bounds (Grob *me, Grob **left, Grob **right)
     l++;
 
   vsize r = columns.size ();
     l++;
 
   vsize r = columns.size ();
-  while (r > l && Note_column::has_rests (columns[r-1]))
+  while (r > l && Note_column::has_rests (columns[r - 1]))
     r--;
 
   *left = *right = 0;
     r--;
 
   *left = *right = 0;
@@ -479,7 +485,7 @@ Tuplet_bracket::get_bounds (Grob *me, Grob **left, Grob **right)
   if (l < r)
     {
       *left = columns[l];
   if (l < r)
     {
       *left = columns[l];
-      *right = columns[r-1];
+      *right = columns[r - 1];
     }
 }
 
     }
 }
 
@@ -512,25 +518,25 @@ Tuplet_bracket::calc_position_and_height (Grob *me_grob, Real *offset, Real *dy)
   if (st && !to_boolean (me->get_property ("cross-staff")))
     {
       Real pad = robust_scm2double (me->get_property ("staff-padding"), -1.0);
   if (st && !to_boolean (me->get_property ("cross-staff")))
     {
       Real pad = robust_scm2double (me->get_property ("staff-padding"), -1.0);
-      if  (pad >= 0.0)
-       {
-         staff = st->extent (commony, Y_AXIS) - my_offset;
-         staff.widen (pad);
-       }
+      if (pad >= 0.0)
+        {
+          staff = st->extent (commony, Y_AXIS) - my_offset;
+          staff.widen (pad);
+        }
     }
     }
-  
+
   Direction dir = get_grob_direction (me);
 
   bool equally_long = false;
   Grob *par_beam = parallel_beam (me, columns, &equally_long);
   Direction dir = get_grob_direction (me);
 
   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];
   bool follow_beam = par_beam
   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"));
+                     && get_grob_direction (par_beam) == dir
+                     && !to_boolean (par_beam->get_property ("knee"));
 
   vector<Offset> points;
   if (columns.size ()
 
   vector<Offset> points;
   if (columns.size ()
@@ -538,64 +544,66 @@ 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 ()))
     {
       && 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]),
       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);
+                                Note_column::get_stem (columns.back ()));
 
 
-      *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));
+      Interval poss;
+      Direction side = LEFT;
+      do
+        {
+          // Trigger setting of stem lengths if necessary.
+          if (Grob *beam = Stem::get_beam (stems[side]))
+            (void) beam->get_property ("quantized-positions");
+          poss[side] = stems[side]->extent (stems[side], Y_AXIS)[get_grob_direction (stems[side])]
+                       + stems[side]->get_parent (Y_AXIS)->relative_coordinate (commony, Y_AXIS);
+        }
+      while (flip (&side) != LEFT);
+
+      *dy = poss[RIGHT] - poss[LEFT];
+      points.push_back (Offset (stems[LEFT]->relative_coordinate (commonx, X_AXIS) - x0, poss[LEFT]));
+      points.push_back (Offset (stems[RIGHT]->relative_coordinate (commonx, X_AXIS) - x0, poss[RIGHT]));
     }
   else
     {
       /*
     }
   else
     {
       /*
-       Use outer non-rest columns to determine slope
+        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)
       */
       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;
-       }
+        {
+          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
       else
-       *dy = 0;
+        *dy = 0;
 
       for (vsize i = 0; i < columns.size (); i++)
 
       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;
+        {
+          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]));
-       }
+          points.push_back (Offset (x, note_ext[dir]));
+        }
     }
 
   if (!follow_beam)
     }
 
   if (!follow_beam)
@@ -603,7 +611,7 @@ Tuplet_bracket::calc_position_and_height (Grob *me_grob, Real *offset, Real *dy)
       points.push_back (Offset (x0 - x0, staff[dir]));
       points.push_back (Offset (x1 - x0, staff[dir]));
     }
       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.
@@ -617,29 +625,34 @@ Tuplet_bracket::calc_position_and_height (Grob *me_grob, Real *offset, Real *dy)
       Interval tuplet_y (tuplets[i]->extent (commony, Y_AXIS));
 
       if (!tuplets[i]->is_live ())
       Interval tuplet_y (tuplets[i]->extent (commony, Y_AXIS));
 
       if (!tuplets[i]->is_live ())
-       continue;
-      
+        continue;
+
       Direction d = LEFT;
       Direction d = LEFT;
-      Drul_array<Real> positions = robust_scm2interval (tuplets[i]->get_property ("positions"),
-                                                       Interval (0,0));
+      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
-       {
-         Real y
-           = tuplet_y.linear_combination (d * sign (other_dy));
-
-         /*
-           We don't take padding into account for nested tuplets.
-           the edges can come very close to the stems, likewise for
-           nested tuplets?
-         */
-
-         points.push_back (Offset (tuplet_x[d] - x0, y));
-       }
+        {
+          Real y
+            = tuplet_y.linear_combination (d * sign (other_dy));
+
+          /*
+            We don't take padding into account for nested tuplets.
+            the edges can come very close to the stems, likewise for
+            nested tuplets?
+          */
+
+          points.push_back (Offset (tuplet_x[d] - x0, y));
+        }
       while (flip (&d) != LEFT);
       while (flip (&d) != LEFT);
+      // Check for number-on-bracket collisions
+      Grob *number = unsmob_grob (tuplets[i]->get_object ("tuplet-number"));
+      if (number)
+        points.push_back (Offset (number->extent (commonx, X_AXIS).center () - x0,
+                                  number->extent (commony, Y_AXIS)[dir]));
     }
 
   *offset = -dir * infinity_f;
     }
 
   *offset = -dir * infinity_f;
@@ -650,7 +663,7 @@ Tuplet_bracket::calc_position_and_height (Grob *me_grob, Real *offset, Real *dy)
       Real tuplety = (*dy) * x * factor + my_offset;
 
       if (points[i][Y_AXIS] * dir > (*offset + tuplety) * dir)
       Real tuplety = (*dy) * x * factor + my_offset;
 
       if (points[i][Y_AXIS] * dir > (*offset + tuplety) * dir)
-       *offset = points[i][Y_AXIS] - tuplety;
+        *offset = points[i][Y_AXIS] - tuplety;
     }
 
   *offset += scm_to_double (me->get_property ("padding")) * dir;
     }
 
   *offset += scm_to_double (me->get_property ("padding")) * dir;
@@ -661,7 +674,7 @@ 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
     This doesn't seem to support cross-staff tuplets atm.
   */
   if (*dy == 0
@@ -672,13 +685,12 @@ Tuplet_bracket::calc_position_and_height (Grob *me_grob, Real *offset, Real *dy)
 
       *offset = rint (*offset);
       if (Staff_symbol_referencer::on_line (me, (int) rint (*offset)))
 
       *offset = rint (*offset);
       if (Staff_symbol_referencer::on_line (me, (int) rint (*offset)))
-       *offset += dir;
+        *offset += dir;
 
       *offset *= 0.5 * ss;
     }
 }
 
 
       *offset *= 0.5 * ss;
     }
 }
 
-
 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)
@@ -697,10 +709,10 @@ Tuplet_bracket::calc_positions (SCM smob)
   Real dy = 0.0;
   Real offset = 0.0;
   calc_position_and_height (me, &offset, &dy);
   Real dy = 0.0;
   Real offset = 0.0;
   calc_position_and_height (me, &offset, &dy);
-  
+
   SCM x = scm_cons (scm_from_double (offset),
   SCM x = scm_cons (scm_from_double (offset),
-                   scm_from_double (offset + dy));
-  
+                    scm_from_double (offset + dy));
+
   return x;
 }
 
   return x;
 }
 
@@ -715,12 +727,37 @@ Tuplet_bracket::get_default_dir (Grob *me)
   for (vsize i = 0; i < columns.size (); i++)
     {
       Grob *nc = columns[i];
   for (vsize i = 0; i < columns.size (); i++)
     {
       Grob *nc = columns[i];
+      if (Note_column::has_rests (nc))
+        continue;
       Direction d = Note_column::dir (nc);
       if (d)
       Direction d = Note_column::dir (nc);
       if (d)
-       dirs[d]++;
+        dirs[d]++;
     }
 
     }
 
-  return dirs[UP] >= dirs[DOWN] ? UP : DOWN;
+  if (dirs[UP] == dirs[DOWN])
+    {
+      if (dirs[UP] == 0)
+        return UP;
+      Grob *staff = Staff_symbol_referencer::get_staff_symbol (me);
+      if (!staff)
+        return UP;
+      Interval staff_extent = staff->extent (staff, Y_AXIS);
+      Interval extremal_positions;
+      extremal_positions.set_empty ();
+      for (vsize i = 0; i < columns.size (); i++)
+        {
+          Direction d = Note_column::dir (columns[i]);
+          extremal_positions[d] = minmax (d, 1.0 * Note_column::head_positions_interval (columns[i])[d], extremal_positions[d]);
+        }
+      Direction d = LEFT;
+      do
+        extremal_positions[d] = -d * (staff_extent[d] - extremal_positions[d]);
+      while (flip (&d) != LEFT);
+
+      return extremal_positions[UP] <= extremal_positions[DOWN] ? UP : DOWN;
+    }
+
+  return dirs[UP] > dirs[DOWN] ? UP : DOWN;
 }
 
 void
 }
 
 void
@@ -741,56 +778,57 @@ SCM
 Tuplet_bracket::calc_cross_staff (SCM smob)
 {
   Grob *me = unsmob_grob (smob);
 Tuplet_bracket::calc_cross_staff (SCM smob)
 {
   Grob *me = unsmob_grob (smob);
-  Grob *staff_symbol = 0;
   extract_grob_set (me, "note-columns", cols);
   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);
 
   bool equally_long = false;
   Grob *par_beam = parallel_beam (me, cols, &equally_long);
 
-  if (par_beam)
-    return par_beam->get_property ("cross-staff");
+  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"));
 
   for (vsize i = 0; i < cols.size (); i++)
     {
       Grob *stem = unsmob_grob (cols[i]->get_object ("stem"));
-      if (!stem)
-       continue;
-      
-      if (to_boolean (stem->get_property ("cross-staff")))
-       return SCM_BOOL_T;
-
-      Grob *stem_staff = Staff_symbol_referencer::get_staff_symbol (stem);
-      if (staff_symbol && (stem_staff != staff_symbol))
+      if (stem && to_boolean (stem->get_property ("cross-staff")))
         return SCM_BOOL_T;
         return SCM_BOOL_T;
-      staff_symbol = stem_staff;
     }
     }
+
   return SCM_BOOL_F;
 }
 
 ADD_INTERFACE (Tuplet_bracket,
   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 "
-              "bracket-visibility "
-              "break-overshoot "
-              "connect-to-neighbor "
-              "control-points "
-              "direction "
-              "edge-height "
-              "edge-text "
-              "gap "
-              "positions "
-              "note-columns "
-              "padding "
-              "tuplet-number "
-              "shorten-pair "
-              "staff-padding "
-              "thickness "
-              "tuplets "
-              );
-
-
+               "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 "
+               "bracket-visibility "
+               "break-overshoot "
+               "connect-to-neighbor "
+               "direction "
+               "edge-height "
+               "edge-text "
+               "full-length-padding "
+               "full-length-to-extent "
+               "gap "
+               "positions "
+               "note-columns "
+               "padding "
+               "tuplet-number "
+               "shorten-pair "
+               "staff-padding "
+               "thickness "
+               "tuplets "
+               "X-positions "
+              );