+
+ extract_grob_set (tuplet, "note-columns", cols);
+ Grob *ref_stem = select_reference_stem (me, cols);
+
+ /*
+ Return bracket-based positioning.
+ */
+ if (!ref_stem
+ || !knee_position_against_beam (me, ref_stem))
+ {
+ Interval x_positions;
+ x_positions = robust_scm2interval (tuplet->get_property ("X-positions"),
+ Interval (0.0, 0.0));
+ return scm_from_double (x_positions.center ());
+ }
+
+ /*
+ Horizontally center the number on the beam.
+ */
+ Real col_pos = left_bound->relative_coordinate (commonx, X_AXIS);
+ Real x_offset = bound_poss.center () - col_pos;
+
+ /*
+ Consider possible collisions with adjacent note columns.
+ */
+ Drul_array<Grob *> adj_cols = adjacent_note_columns (me, ref_stem);
+ Interval number_ext = me->extent (commonx, X_AXIS);
+ number_ext.translate (x_offset);
+ Real padding = robust_scm2double (me->get_property ("padding"), 0.5);
+ number_ext.widen (padding);
+
+ Interval cor (0.0, 0.0);
+
+ for (LEFT_and_RIGHT (d))
+ if (adj_cols[d])
+ {
+ Interval nc_ext = adj_cols[d]->extent (commonx, X_AXIS);
+ Interval overlap (nc_ext);
+ overlap.intersect (number_ext);
+ if (!overlap.is_empty ())
+ cor[d] = overlap.length () * -d;
+ x_offset += cor[d];
+ }
+
+ return scm_from_double (x_offset);
+}
+
+/*
+ When a number is placed against the beam (independently of a bracket), the
+ Y-extent of a reference stem is used to determine the vertical placement of
+ the number. When French beams are used the stem may not reach all beams.
+*/
+int
+count_beams_not_touching_stem (SCM beaming)
+{
+ int count = 0;
+
+ for (SCM s = scm_car (beaming); scm_is_pair (s); s = scm_cdr (s))
+ {
+ if (scm_is_true (ly_memv (scm_car (s), scm_cdr (beaming))))
+ ++count;
+ }
+
+ return max (0, count - 1);
+}
+
+MAKE_SCHEME_CALLBACK (Tuplet_number, calc_y_offset, 1);
+SCM
+Tuplet_number::calc_y_offset (SCM smob)
+{
+ Spanner *me = unsmob<Spanner> (smob);
+ Spanner *tuplet = unsmob<Spanner> (me->get_object ("bracket"));
+ Drul_array<Real> positions = robust_scm2drul (tuplet->get_property ("positions"),
+ Drul_array<Real> (0.0, 0.0));
+ SCM to_bracket = scm_from_double ((positions[LEFT] + positions[RIGHT]) / 2.0);
+
+ Grob *commonx = Tuplet_bracket::get_common_x (me);
+ commonx = commonx->common_refpoint (me, X_AXIS);
+ Real x_coord = me->relative_coordinate (commonx, X_AXIS);
+ extract_grob_set (tuplet, "note-columns", columns);
+ Grob *ref_stem = select_reference_stem (me, columns);
+
+ if (!ref_stem || !knee_position_against_beam (me, ref_stem))
+ return to_bracket;
+
+ /*
+ First, we calculate the Y-offset of the tuplet number as if it
+ is positioned at the reference stem.
+ */
+ Grob *commony = common_refpoint_of_array (columns, tuplet, Y_AXIS);
+ commony = commony->common_refpoint (me, Y_AXIS);
+ extract_grob_set (me, "tuplets", tuplets);
+ 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);
+
+ Interval ref_stem_ext = ref_stem->extent (commony, Y_AXIS);
+ Real tuplet_y = tuplet->relative_coordinate (commony, Y_AXIS);
+ Direction ref_stem_dir = get_grob_direction (ref_stem);
+
+ Real y_offset = ref_stem_ext[ref_stem_dir] - tuplet_y;
+
+ /*
+ Additional displacement for French beaming.
+ */
+ if (to_boolean (ref_stem->get_property ("french-beaming")))