+ Spanner *me = dynamic_cast<Spanner *> (me_grob);
+
+ int col_count = cols.size ();
+
+ if (!col_count)
+ return 0;
+
+ /*
+ When we have an odd number of stems, we choose the middle stem as
+ our reference.
+ */
+ Grob *ref_stem = Note_column::get_stem (cols[col_count / 2]);
+
+ if (col_count % 2 == 1)
+ return ref_stem;
+
+ /*
+ When we have an even number of stems, we choose between the central
+ two stems.
+ */
+ Direction me_dir = robust_scm2dir (me->get_property ("direction"), UP);
+ Drul_array<Item *> bounding_stems (Note_column::get_stem (cols[col_count / 2 - 1]),
+ Note_column::get_stem (cols[col_count / 2]));
+
+ for (LEFT_and_RIGHT (d))
+ if (!bounding_stems[d])
+ return bounding_stems[-d];
+
+ /*
+ If the central stems point in opposite directions, the number may
+ be placed on either side unless there is a fractional beam, in which
+ case the number goes opposite to the partial beam.
+
+ When there is an option, we use the setting of TupletNumber.direction.
+
+ If the central stems are in the same direction, it doesn't matter
+ which is used as the reference. We use the one on the left.
+ */
+ Direction dir_left = get_grob_direction (bounding_stems[LEFT]);
+ Direction dir_right = get_grob_direction (bounding_stems[RIGHT]);
+
+ if (dir_left == dir_right)
+ ref_stem = bounding_stems[LEFT];
+ else
+ {
+ int beam_count_L_R = Stem::get_beaming (bounding_stems[LEFT], RIGHT);
+ int beam_count_R_L = Stem::get_beaming (bounding_stems[RIGHT], LEFT);
+ if (beam_count_L_R == beam_count_R_L)
+ ref_stem = (dir_left == me_dir) ? bounding_stems[LEFT] : bounding_stems[RIGHT];
+ else
+ ref_stem = (beam_count_L_R > beam_count_R_L)
+ ? bounding_stems[LEFT] : bounding_stems[RIGHT];
+ }
+
+ return ref_stem;
+}
+
+/*
+ When we place the number close to the beam, we need to consider the note
+ columns adjoining the tuplet number on the same side of the beam. The
+ number may not fit in the available space, or may need to be shifted
+ horizontally out of the way of stems and ledger lines.
+*/
+Drul_array<Grob *>
+Tuplet_number::adjacent_note_columns (Grob *me_grob, Grob *ref_stem)
+{
+ Spanner *me = dynamic_cast<Spanner *> (me_grob);
+ Spanner *tuplet = unsmob_spanner (me->get_object ("bracket"));
+
+ extract_grob_set (tuplet, "note-columns", columns);
+ Grob *ref_col = ref_stem->get_parent (X_AXIS); // X-parent of Stem = NoteColumn
+ Direction ref_stem_dir = get_grob_direction (ref_stem);
+ vector<Grob *> filtered_cols;
+ vsize ref_pos = 0;
+
+ for (vsize i = 0, counter = 0; i < columns.size (); ++i)
+ {
+ Grob *stem = Note_column::get_stem (columns[i]);
+ if (stem && get_grob_direction (stem) == -ref_stem_dir)
+ {
+ filtered_cols.push_back (columns[i]);
+ ++counter;
+ }
+ if (columns[i] == ref_col)
+ {
+ filtered_cols.push_back (columns[i]);
+ ref_pos = counter;
+ }
+ }
+
+ Drul_array<Grob *> adj_cols (0, 0);
+
+ if (ref_pos > 0)
+ adj_cols[LEFT] = filtered_cols[ref_pos - 1];
+ if (ref_pos < filtered_cols.size () - 1)
+ adj_cols[RIGHT] = filtered_cols[ref_pos + 1];
+
+ return adj_cols;
+}
+
+/*
+ We determine whether our tuplet number will be put next to the beam
+ independently of the positioning of the associated tuplet bracket.
+
+ Draw next to the beam if:
+ --bracket isn't visible, AND
+ --there is a beam above or below the number, AND
+ --this beam is kneed, AND
+ --the tuplet number will fit between adjoining note columns
+*/
+bool
+Tuplet_number::knee_position_against_beam (Grob *me_grob, Grob *ref_stem)
+{
+ Spanner *me = dynamic_cast<Spanner *> (me_grob);
+ Spanner *tuplet = unsmob_spanner (me->get_object ("bracket"));
+
+ bool bracket_visible = to_boolean (me->get_property ("bracket-visibility"))
+ || !tuplet->extent (tuplet, Y_AXIS).is_empty ();
+
+ if (bracket_visible || !to_boolean (me->get_property ("knee-to-beam")))
+ return false;
+
+ Grob *beam = Stem::get_beam (ref_stem);
+
+ if (!beam || !Beam::is_knee (beam))
+ return false;
+
+ Grob *commonx = Tuplet_bracket::get_common_x (tuplet);
+ commonx = commonx->common_refpoint (me, X_AXIS);
+
+ Interval number_ext = me->extent (commonx, X_AXIS);
+
+ Drul_array<Grob *> adj_cols = adjacent_note_columns (me, ref_stem);
+
+ Item *left = me->get_bound (LEFT);
+ Item *right = me->get_bound (RIGHT);
+
+ if (!left || !right)
+ return false;
+
+ Drul_array<Item *> bounds (left, right);
+
+ Interval available_ext;
+ Real padding = robust_scm2double (me->get_property ("padding"), 0.5);
+
+ /*
+ If there is no note column on a given side of the tuplet number, we use
+ a paper column instead to determine the available space. Padding is only
+ considered in the case of a note column.
+ */
+ for (LEFT_and_RIGHT (d))
+ {
+ if (adj_cols[d])
+ available_ext[d] = Axis_group_interface::generic_bound_extent (adj_cols[d], commonx, X_AXIS)[-d] + (-d * padding);
+ else
+ available_ext[d] = Axis_group_interface::generic_bound_extent (bounds[d], commonx, X_AXIS)[-d];
+ }
+
+ if (number_ext.length () > available_ext.length ())
+ {
+ programming_error ("not enough space for tuplet number against beam");
+ return false;
+ }
+
+ return true;
+}