source file of the GNU LilyPond music typesetter
- (c) 1997--2006 Jan Nieuwenhuizen <janneke@gnu.org>
+ (c) 1997--2009 Jan Nieuwenhuizen <janneke@gnu.org>
Han-Wen Nienhuys <hanwen@xs4all.nl>
*/
#include "spanner.hh"
#include "staff-symbol-referencer.hh"
#include "lookup.hh"
+#include "paper-column.hh"
+#include "moment.hh"
static Item *
get_x_bound_item (Grob *me_grob, Direction hdir, Direction my_dir)
Direction xdir, SCM sym)
{
Drul_array<Real> zero (0, 0);
- Drul_array<Real> pair = robust_scm2drul (me->internal_get_property (sym), zero);
+ 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 *
-Tuplet_bracket::parallel_beam (Grob *me_grob, Link_array__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);
Drul_array<Grob*> stems (Note_column::get_stem (cols[0]),
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;
Drul_array<Grob*> beams;
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];
}
-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)
{
do
{
Direction break_dir = bounds[d]->break_status_dir ();
-
Spanner *orig_spanner = dynamic_cast<Spanner*> (me->original ());
-
vsize neighbor_idx = me->get_break_index () - break_dir;
if (break_dir
&& d == RIGHT
connect_to_other[d]
= (break_dir
- && (neighbor_idx < orig_spanner->broken_intos_.size ()
- && neighbor_idx >= 0)
+ && neighbor_idx < orig_spanner->broken_intos_.size ()
&& orig_spanner->broken_intos_[neighbor_idx]->is_live ());
}
while (flip (&d) != LEFT);
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)
{
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
- = 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);
x_span[d] = robust_relative_extent (bounds[d], commonx, X_AXIS)[RIGHT]
- overshoot[LEFT];
}
+
else if (d == RIGHT
&& (columns.empty ()
|| (bounds[d]->get_column ()
!= dynamic_cast<Item *> (columns.back ())->get_column ())))
{
/*
- TODO: make padding tunable?
+ We're connecting to a column, for the last bit of a broken
+ fullLength bracket.
*/
- 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;
-
- 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);
bracket_visibility = ly_scm2bool (bracket);
else if (bracket == ly_symbol2scm ("if-no-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)
{
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));
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"));
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
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]));
}
+
while (flip (&d) != LEFT);
+ if (gap.is_empty ())
+ m.add_stencil (Line_interface::line (me, straight_corners[LEFT],
+ straight_corners[RIGHT]));
+
return m;
}
while (l < columns.size () && Note_column::has_rests (columns[l]))
l++;
- int r = columns.size ()- 1;
- while (r >= l && Note_column::has_rests (columns[r]))
+ vsize r = columns.size ();
+ while (r > l && Note_column::has_rests (columns[r-1]))
r--;
*left = *right = 0;
- if (l <= r)
+ if (l < r)
{
*left = columns[l];
- *right = columns[r];
+ *right = columns[r-1];
}
}
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;
- 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)
{
- staff = st->extent (commony, Y_AXIS);
+ staff = st->extent (commony, Y_AXIS) - my_offset;
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;
-
- *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];
-
- std::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++)
+ bool follow_beam = par_beam
+ && get_grob_direction (par_beam) == dir
+ && ! to_boolean (par_beam->get_property ("knee"));
+
+ vector<Offset> points;
+ 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.
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;
- 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];
Real y
= tuplet_y.linear_combination (d * sign (other_dy));
-#if 0
/*
- Let's not take padding into account for nested tuplets.
+ We don't take padding into account for nested tuplets.
the edges can come very close to the stems, likewise for
nested tuplets?
*/
- Drul_array<Real> my_height
- = robust_scm2drul (me->get_property ("edge-height"),
- Interval (0, 0));
- if (dynamic_cast<Spanner *> (tuplets[i])->get_bound (d)
- == me->get_bound (d))
- y += dir * my_height[d];
-#endif
points.push_back (Offset (tuplet_x[d] - x0, y));
}
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 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;
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))
SCM
Tuplet_bracket::calc_positions (SCM smob)
{
- Grob *me = unsmob_grob (smob);
- extract_grob_set (me, "note-columns", columns);
-
- Direction dir = get_grob_direction (me);
- bool equally_long = false;
- Grob *par_beam = parallel_beam (me, columns, &equally_long);
+ Spanner *me = unsmob_spanner (smob);
- /*
- We follow the beam only if there is one, and we are next to it.
- */
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");
-
- Real lp = scm_to_double (scm_car (ps));
- Real rp = scm_to_double (scm_cdr (ps));
-
- /*
- duh. magic.
- */
- offset = lp + dir * (0.5 + scm_to_double (me->get_property ("padding")));
- dy = rp - lp;
- }
-
+ calc_position_and_height (me, &offset, &dy);
SCM x = scm_cons (scm_from_double (offset),
scm_from_double (offset + dy));
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);
+ Grob *staff_symbol = 0;
+ extract_grob_set (me, "note-columns", cols);
+ bool equally_long = false;
+ Grob *par_beam = parallel_beam (me, cols, &equally_long);
+ if (par_beam)
+ return par_beam->get_property ("cross-staff");
+
+ 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))
+ return SCM_BOOL_T;
+ staff_symbol = stem_staff;
+ }
+ 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 "
"direction "
"edge-height "
"edge-text "
+ "full-length-padding "
+ "full-length-to-extent "
"gap "
"positions "
"note-columns "
"shorten-pair "
"staff-padding "
"thickness "
- "tuplets ");
+ "tuplets "
+ );