+ 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")))
+ {
+ Grob *beam = Stem::get_beam (ref_stem);
+ Real beam_translation = Beam::get_beam_translation (beam);
+ SCM beaming = ref_stem->get_property ("beaming");
+ y_offset += ref_stem_dir
+ * count_beams_not_touching_stem (beaming)
+ * beam_translation;
+ }
+
+ Real padding = robust_scm2double (me->get_property ("padding"), 0.5);
+ Real num_height = me->extent (commony, Y_AXIS).length ();
+
+ y_offset += ref_stem_dir * (padding + num_height / 2.0);
+
+ /*
+ Now we adjust the vertical position of the number to reflect
+ its actual horizontal placement along the beam.
+ */
+ Real ref_stem_x = ref_stem->relative_coordinate (commonx, X_AXIS);
+ y_offset += calc_beam_y_shift (ref_stem, x_coord - ref_stem_x);
+
+ /*
+ Check if the number is between the beam and the staff. If so, it will collide
+ with ledger lines. Move it into the staff.
+ */
+ if (Grob *st = Staff_symbol_referencer::get_staff_symbol (ref_stem))
+ {
+ Interval staff_ext_y = st->extent (commony, Y_AXIS);
+ bool move = ref_stem_dir == DOWN
+ ? ref_stem_ext[DOWN] > staff_ext_y[UP]
+ : staff_ext_y[DOWN] > ref_stem_ext[UP];
+ if (move)
+ {
+ Interval ledger_domain = Interval (min (staff_ext_y[UP], ref_stem_ext[UP]),
+ max (staff_ext_y[DOWN], ref_stem_ext[DOWN]));
+ Interval num_y (me->extent (commony, Y_AXIS));
+ num_y.translate (y_offset);
+ Interval num_ledger_overlap (num_y);
+ num_ledger_overlap.intersect (ledger_domain);
+ Real line_thickness = Staff_symbol_referencer::line_thickness (st);
+ Real staff_space = Staff_symbol_referencer::staff_space (st);
+ // Number will touch outer staff line.
+ if (!num_ledger_overlap.is_empty ()
+ && num_ledger_overlap.length () > (staff_space / 2.0)
+ && move)
+ y_offset += staff_ext_y[-ref_stem_dir] - num_y[-ref_stem_dir]
+ + line_thickness * ref_stem_dir;
+ }
+ }
+
+ /*
+ Now consider possible collisions with accidentals on the right. We
+ move the accidental away from the beam.
+ */
+ Drul_array<Grob *> adj_cols = adjacent_note_columns (me, ref_stem);
+
+ if (!adj_cols[RIGHT])
+ return scm_from_double (y_offset);
+
+ /*
+ Collect Y-extents of accidentals that overlap the number
+ along the X-axis.
+ */
+ extract_grob_set (adj_cols[RIGHT], "note-heads", heads);
+ Interval colliding_acc_ext_y;
+
+ for (vsize i = 0; i < heads.size (); i++)
+ if (Grob *acc = unsmob<Grob> (heads[i]->get_object ("accidental-grob")))
+ {
+ commony = commony->common_refpoint (acc, Y_AXIS);
+ Interval acc_ext_y = acc->extent (commony, Y_AXIS);
+
+ commonx = commonx->common_refpoint (acc, X_AXIS);
+ Interval num_ext_x = me->extent (commonx, X_AXIS);
+ num_ext_x.widen (padding);
+ Interval overlap_x (num_ext_x);
+ Interval acc_x = acc->extent (commonx, X_AXIS);
+ overlap_x.intersect (acc_x);
+
+ if (!overlap_x.is_empty ())
+ colliding_acc_ext_y.unite (acc_ext_y);
+ }
+ /*
+ Does our number intersect vertically with the accidental Y-extents we
+ combined above? If so, move it.
+ */
+ Interval overlap_acc_y (colliding_acc_ext_y);
+ Interval num_ext_y (me->extent (commony, Y_AXIS));
+ num_ext_y.translate (y_offset);
+ overlap_acc_y.intersect (num_ext_y);