/*
- plet-spanner.cc -- implement Tuplet_bracket
+ tuplet-bracket.cc -- implement Tuplet_bracket
source file of the GNU LilyPond music typesetter
- (c) 1997--2003 Jan Nieuwenhuizen <janneke@gnu.org>
+ (c) 1997--2006 Jan Nieuwenhuizen <janneke@gnu.org>
+ Han-Wen Nienhuys <hanwen@xs4all.nl>
*/
/*
- There is no support for kneed brackets, or nested brackets.
- number placement for parallel beams should be much more advanced:
- for sloped beams some extra horizontal offset must be introduced.
+ for sloped beams some extra horizontal offset must be introduced.
- number placement is usually done over the center note, not the
- graphical center.
-
- */
+ graphical center.
+*/
+
+/*
+ TODO: quantise, we don't want to collide with staff lines.
+ (or should we be above staff?)
+
+ todo: handle breaking elegantly.
+*/
-#include <math.h>
+#include "tuplet-bracket.hh"
#include "line-interface.hh"
#include "beam.hh"
-#include "box.hh"
#include "warn.hh"
+#include "output-def.hh"
#include "font-interface.hh"
-#include "molecule.hh"
-#include "paper-def.hh"
-#include "text-item.hh"
-#include "tuplet-bracket.hh"
+#include "text-interface.hh"
#include "stem.hh"
#include "note-column.hh"
-#include "group-interface.hh"
+#include "pointer-group-interface.hh"
#include "directional-element-interface.hh"
#include "spanner.hh"
#include "staff-symbol-referencer.hh"
#include "lookup.hh"
-
-static Grob*
-get_x_bound_grob (Grob *g, Direction my_dir)
+static Item *
+get_x_bound_item (Grob *me_grob, Direction hdir, Direction my_dir)
{
- if (Note_column::get_stem (g)
+ Spanner *me = dynamic_cast<Spanner *> (me_grob);
+ Item *g = me->get_bound (hdir);
+ if (Note_column::has_interface (g)
+ && Note_column::get_stem (g)
&& Note_column::dir (g) == my_dir)
- {
- g = Note_column::get_stem (g);
- }
+ g = Note_column::get_stem (g);
+
return g;
}
+void
+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;
+
+ me->internal_set_property (sym, ly_interval2scm (pair));
+}
+
-Grob*
-Tuplet_bracket::parallel_beam (Grob *me, Link_array<Grob> const &cols, bool *equally_long)
+Grob *
+Tuplet_bracket::parallel_beam (Grob *me_grob, Link_array<Grob> const &cols, bool *equally_long)
{
- /*
- ugh: code dup.
- */
- Grob *s1 = Note_column::get_stem (cols[0]);
- Grob *s2 = Note_column::get_stem (cols.top());
+ Spanner *me = dynamic_cast<Spanner *> (me_grob);
- Grob*b1 = s1 ? Stem::get_beam (s1) : 0;
- Grob*b2 = s2 ? Stem::get_beam (s2) : 0;
-
- Spanner*sp = dynamic_cast<Spanner*> (me);
+ if (me->get_bound (LEFT)->break_status_dir ()
+ || me->get_bound (RIGHT)->break_status_dir ())
+ return 0;
- *equally_long= false;
- if (! (b1 && (b1 == b2) && !sp->broken_b()))
- return 0;
+ Drul_array<Grob*> stems (Note_column::get_stem (cols[0]),
+ Note_column::get_stem (cols.top ()));
+
+ if (dynamic_cast<Item*> (stems[RIGHT])->get_column ()
+ != me->get_bound (RIGHT)->get_column())
+ return 0;
- Link_array<Grob> beam_stems = Pointer_group_interface__extract_grobs
- (b1, (Grob*)0, "stems");
+ Drul_array<Grob*> beams;
+ Direction d = LEFT;
+ do {
+ beams[d] = stems[d] ? Stem::get_beam (stems[d]) : 0;
+ } while (flip (&d) != LEFT);
+
+ *equally_long = false;
+ if (! (beams[LEFT] && (beams[LEFT] == beams[RIGHT]) && !me->is_broken ()))
+ return 0;
- if (beam_stems.size() == 0)
+ extract_grob_set (beams[LEFT], "stems", beam_stems);
+ if (beam_stems.size () == 0)
{
- programming_error ("Beam under tuplet bracket has no stems!");
+ programming_error ("beam under tuplet bracket has no stems");
*equally_long = 0;
return 0;
}
+
+ *equally_long = (beam_stems[0] == stems[LEFT] && beam_stems.top () == stems[RIGHT]);
+ return beams[LEFT];
+}
+
+
+MAKE_SCHEME_CALLBACK(Tuplet_bracket,calc_connect_to_neighbors,1);
+SCM
+Tuplet_bracket::calc_connect_to_neighbors (SCM smob)
+{
+ Spanner *me = unsmob_spanner (smob);
+
+ Direction dir = get_grob_direction (me);
+ Drul_array<Item *> bounds (get_x_bound_item (me, LEFT, dir),
+ get_x_bound_item (me, RIGHT, dir));
- *equally_long = (beam_stems[0] == s1 && beam_stems.top() == s2);
- return b1;
+ 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 ());
+
+ int 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];
+
+ /* trigger possible suicide*/
+ (void) neighbor->get_property ("positions");
+ }
+
+ connect_to_other[d]
+ = (break_dir
+ && (neighbor_idx < orig_spanner->broken_intos_.size ()
+ && neighbor_idx >= 0)
+ && orig_spanner->broken_intos_[neighbor_idx]->is_live ());
+ }
+ while (flip (&d) != 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]));
+
+ return SCM_EOL;
}
+Grob*
+Tuplet_bracket::get_common_x (Spanner *me)
+{
+ extract_grob_set (me, "note-columns", columns);
-/*
- TODO:
+ 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);
- in the case that there is no bracket, but there is a (single) beam,
- follow beam precisely for determining tuplet number location.
+ return commonx;
+}
- */
-MAKE_SCHEME_CALLBACK (Tuplet_bracket,brew_molecule,1);
+MAKE_SCHEME_CALLBACK(Tuplet_bracket,calc_control_points,1)
SCM
-Tuplet_bracket::brew_molecule (SCM smob)
+Tuplet_bracket::calc_control_points (SCM smob)
{
- Grob *me= unsmob_grob (smob);
- Molecule mol;
- Link_array<Grob> columns=
- Pointer_group_interface__extract_grobs (me, (Grob*)0, "note-columns");
+ Spanner *me = unsmob_spanner (smob);
- if (!columns.size ())
- return mol.smobbed_copy ();
+ extract_grob_set (me, "note-columns", columns);
+ Drul_array<Real> positions
+ = ly_scm2realdrul (me->get_property ("positions"));
+
+ Grob *commonx = get_common_x (me);
+ Direction dir = get_grob_direction (me);
+
+ Drul_array<Item *> bounds;
+ 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));
+
+ Interval x_span;
+ Direction d = LEFT;
+ do
+ {
+ x_span[d] = robust_relative_extent (bounds[d], commonx, X_AXIS)[d];
- {
- SCM lp = me->get_grob_property ("left-position");
- SCM rp = me->get_grob_property ("right-position");
+ 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];
+ }
+ else if (d == RIGHT
+ && (columns.is_empty ()
+ || (bounds[d]->get_column ()
+ != dynamic_cast<Item *> (columns.top ())->get_column ())))
+ {
+ /*
+ 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;
+ }
+ }
+ while (flip (&d) != LEFT);
- if (!gh_number_p (rp) || !gh_number_p (lp))
- after_line_breaking (smob);
- }
- Real ly = robust_scm2double (me->get_grob_property ("left-position"), 0);
- Real ry = robust_scm2double (me->get_grob_property ("right-position"), 0);
- bool equally_long = false;
- Grob * par_beam = parallel_beam (me, columns, &equally_long);
+ 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])));
+}
- Spanner*sp = dynamic_cast<Spanner*> (me);
+/*
+ TODO:
- bool bracket_visibility = !(par_beam && equally_long);
- bool number_visibility = true;
+ in the case that there is no bracket, but there is a (single) beam,
+ follow beam precisely for determining tuplet number location.
+*/
+MAKE_SCHEME_CALLBACK (Tuplet_bracket, print, 1);
+SCM
+Tuplet_bracket::print (SCM smob)
+{
+ Spanner *me = unsmob_spanner (smob);
+ Stencil mol;
+ 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);
/*
Fixme: the type of this prop is sucky.
- */
- SCM bracket = me->get_grob_property ("bracket-visibility");
- if (gh_boolean_p (bracket))
- {
- bracket_visibility = gh_scm2bool (bracket);
- }
+ */
+ 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"))
bracket_visibility = !par_beam;
- SCM numb = me->get_grob_property ("number-visibility");
- if (gh_boolean_p (numb))
+
+ SCM cpoints = me->get_property ("control-points");
+ if (scm_ilength (cpoints) < 2)
{
- number_visibility = gh_scm2bool (numb);
+ me->suicide ();
+ return SCM_EOL;
}
- else if (numb == ly_symbol2scm ("if-no-beam"))
- number_visibility = !par_beam;
-
- Grob * commonx = columns[0]->common_refpoint (columns.top (),X_AXIS);
-
- /*
- Tuplet brackets are normally not broken, but we shouldn't crash if
- they are.
- */
- commonx = commonx->common_refpoint (sp->get_bound(LEFT), X_AXIS);
- commonx = commonx->common_refpoint (sp->get_bound(RIGHT), X_AXIS);
- Direction dir = get_grob_direction (me);
-
- Grob * lgr = get_x_bound_grob (columns[0], dir);
- Grob * rgr = get_x_bound_grob (columns.top(), dir);
- Real x0 = lgr->extent (commonx,X_AXIS)[LEFT];
- Real x1 = rgr->extent (commonx,X_AXIS)[RIGHT];
-
- Real w = x1 -x0;
+ Drul_array<Offset> points;
+ points[LEFT] = ly_scm2offset (scm_car (cpoints));
+ points[RIGHT] = ly_scm2offset (scm_cadr (cpoints));
- SCM number = me->get_grob_property ("text");
+ Interval x_span (points[LEFT][X_AXIS], points[RIGHT][X_AXIS]);
+ Drul_array<Real> positions (points[LEFT][Y_AXIS], points[RIGHT][Y_AXIS]);
- Paper_def *pap = me->get_paper ();
- if (gh_string_p (number) && number_visibility)
- {
- SCM properties = Font_interface::font_alist_chain (me);
- SCM snum = Text_item::interpret_markup (pap->self_scm (), properties, number);
- Molecule num = *unsmob_molecule (snum);
- num.align_to (X_AXIS, CENTER);
- num.translate_axis (w/2, X_AXIS);
- num.align_to (Y_AXIS, CENTER);
-
- num.translate_axis ((ry-ly)/2, Y_AXIS);
-
- mol.add_molecule (num);
- }
+ 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.
-
- TODO: should use GAP in calculation too.
- */
- if (bracket_visibility && number_visibility
- && mol.extent (X_AXIS).length () > w)
+ */
+ Real gap = 0.;
+ if (bracket_visibility && number_grob)
{
- bracket_visibility = false;
+ 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;
+ }
}
-
- if (bracket_visibility)
+
+ if (bracket_visibility)
{
- SCM gap = me->get_grob_property ("gap");
-
- SCM fl = me->get_grob_property ("bracket-flare");
- SCM eh = me->get_grob_property ("edge-height");
- SCM sp = me->get_grob_property ("shorten-pair");
+ 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);
+ Drul_array<Real> flare
+ = robust_scm2drul (me->get_property ("bracket-flare"), zero);
+ Drul_array<Real> shorten
+ = robust_scm2drul (me->get_property ("shorten-pair"), zero);
+ Drul_array<Stencil> edge_stencils;
+
+ Direction dir = get_grob_direction (me);
+ 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));
+
Direction d = LEFT;
- Drul_array<Real> height, flare, shorten;
- do {
- flare[d] = height[d] = shorten[d] = 0.0;
- if (is_number_pair (fl))
- flare[d] += gh_scm2double (index_get_cell (fl, d));
- if (is_number_pair (eh))
- height[d] += gh_scm2double (index_get_cell (eh, d)) * - dir;
- if (is_number_pair (sp))
- shorten[d] += gh_scm2double (index_get_cell (sp, d));
- }
+ 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;
+ }
+ }
+ }
+ }
while (flip (&d) != LEFT);
-
- Molecule brack = make_bracket (me, Y_AXIS,
- Offset (w, ry - ly),
- height,
- gh_scm2double (gap),
- flare,
- shorten);
- mol.add_molecule (brack);
+
+ 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);
+
+ do
+ {
+ if (!edge_stencils[d].is_empty ())
+ brack.add_stencil (edge_stencils[d]);
+ }
+ while (flip (&d) != LEFT);
+
+ mol.add_stencil (brack);
}
-
- mol.translate_axis (ly, Y_AXIS);
- mol.translate_axis (x0 - sp->get_bound (LEFT)->relative_coordinate (commonx,X_AXIS),X_AXIS);
+
+ mol.translate (points[LEFT]);
return mol.smobbed_copy ();
}
TODO: this will fail for very short (shorter than the flare)
brackets.
- */
-Molecule
-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,
- Real gap,
+ Interval gap,
Drul_array<Real> flare,
Drul_array<Real> shorten)
{
- Drul_array<Offset> corners (Offset(0,0), dz);
-
+ Drul_array<Offset> corners (Offset (0, 0), dz);
+
Real length = dz.length ();
Drul_array<Offset> gap_corners;
-
Axis bracket_axis = other_axis (protusion_axis);
Drul_array<Offset> straight_corners = corners;
Direction d = LEFT;
- do {
- straight_corners[d] += - d * shorten[d] /length * dz;
- gap_corners[d] = (dz * 0.5) + d * gap / length * dz;
- } while (flip (&d) != LEFT);
+ do
+ straight_corners[d] += -d * shorten[d] / length * dz;
+ 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)
+ ;
Drul_array<Offset> flare_corners = straight_corners;
- do {
- flare_corners[d][bracket_axis] = straight_corners[d][bracket_axis];
- flare_corners[d][protusion_axis] += height[d];
- straight_corners[d][bracket_axis] += - d * flare[d];
- } while (flip (&d) != LEFT);
+ do
+ {
+ flare_corners[d][bracket_axis] = straight_corners[d][bracket_axis];
+ flare_corners[d][protusion_axis] += height[d];
+ straight_corners[d][bracket_axis] += -d * flare[d];
+ }
+ while (flip (&d) != LEFT);
- Molecule m;
- do {
- m.add_molecule (Line_interface::line (me, straight_corners[d],
- gap_corners[d]));
-
- m.add_molecule (Line_interface::line (me, straight_corners[d],
- flare_corners[d]));
- } while (flip (&d) != LEFT);
+ Stencil m;
+ do
+ {
+ m.add_stencil (Line_interface::line (me, straight_corners[d],
+ gap_corners[d]));
- return m;
+ m.add_stencil (Line_interface::line (me, straight_corners[d],
+ flare_corners[d]));
+ }
+ while (flip (&d) != LEFT);
+
+ return m;
}
+void
+Tuplet_bracket::get_bounds (Grob *me, Grob **left, Grob **right)
+{
+ extract_grob_set (me, "note-columns", columns);
+ int l = 0;
+ 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]))
+ r--;
+
+ *left = *right = 0;
+
+ if (l <= r)
+ {
+ *left = columns[l];
+ *right = columns[r];
+ }
+}
/*
use first -> last note for slope, and then correct for disturbing
notes in between. */
void
-Tuplet_bracket::calc_position_and_height (Grob*me,Real *offset, Real * dy)
+Tuplet_bracket::calc_position_and_height (Grob *me_grob, Real *offset, Real *dy)
{
- Link_array<Grob> columns=
- Pointer_group_interface__extract_grobs (me, (Grob*)0, "note-columns");
+ Spanner *me = dynamic_cast<Spanner *> (me_grob);
+ extract_grob_set (me, "note-columns", columns);
+ extract_grob_set (me, "tuplets", tuplets);
- SCM cols = me->get_grob_property ("note-columns");
- Grob * commony = common_refpoint_of_list (cols, me, Y_AXIS);
- Grob * commonx = common_refpoint_of_list (cols, me, X_AXIS);
+ Grob *commony = common_refpoint_of_array (columns, 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);
- Interval staff;
+ Grob *commonx = get_common_x (me);
+ commonx = common_refpoint_of_array (tuplets, commonx, Y_AXIS);
- if (Grob * st = Staff_symbol_referencer::get_staff_symbol (me))
- staff = st->extent (commony, Y_AXIS);
+ Interval staff;
+ if (Grob *st = Staff_symbol_referencer::get_staff_symbol (me))
+ {
+ Real pad = robust_scm2double (me->get_property ("staff-padding"), -1.0);
+ if (pad >= 0.0)
+ {
+ staff = st->extent (commony, Y_AXIS);
+ staff.widen (pad);
+ }
+ }
Direction dir = get_grob_direction (me);
/*
Use outer non-rest columns to determine slope
- */
- int l = 0;
- while (l <columns.size () && Note_column::rest_b (columns[l]))
- l ++;
-
- int r = columns.size ()- 1;
- while (r >= l && Note_column::rest_b (columns[r]))
- r--;
-
- if (l < r)
+ */
+ Grob *left_col = 0;
+ Grob *right_col = 0;
+ get_bounds (me, &left_col, &right_col);
+ if (left_col && right_col)
{
- Interval rv =columns[r]->extent (commony, Y_AXIS);
- Interval lv =columns[l]->extent (commony, Y_AXIS);
+ Interval rv = right_col->extent (commony, Y_AXIS);
+ Interval lv = left_col->extent (commony, Y_AXIS);
rv.unite (staff);
lv.unite (staff);
-
- *dy = rv[dir] - lv[dir];
+ 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;
-
+ *dy = 0;
- *offset = - dir * infinity_f;
+ *offset = -dir * infinity_f;
- if (!columns.size ())
- return;
+ 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];
+ Array<Offset> points;
+ points.push (Offset (x0 - x0, staff[dir]));
+ points.push (Offset (x1 - x0, staff[dir]));
-
- Grob * lgr = get_x_bound_grob (columns[0], dir);
- Grob * rgr = get_x_bound_grob (columns.top(), dir);
- Real x0 = lgr->extent (commonx,X_AXIS)[LEFT];
- Real x1 = rgr->extent (commonx,X_AXIS)[RIGHT];
+ for (int i = 0; i < columns.size (); i++)
+ {
+ Interval note_ext = columns[i]->extent (commony, Y_AXIS);
+ Real notey = note_ext[dir] - me->relative_coordinate (commony, Y_AXIS);
+ Real x = columns[i]->relative_coordinate (commonx, X_AXIS) - x0;
+ points.push (Offset (x, notey));
+ }
/*
- Slope.
+ This is a slight hack. We compute two encompass points from the
+ bbox of the smaller tuplets.
+
+ We assume that the smaller bracket is 1.0 space high.
*/
- Real factor = columns.size () > 1 ? 1/ (x1 - x0) : 1.0;
-
- for (int i = 0; i < columns.size (); i++)
+ Real ss = Staff_symbol_referencer::staff_space (me);
+ for (int i = 0; i < tuplets.size (); i++)
{
- Interval note_ext =columns[i]->extent (commony, Y_AXIS);
- note_ext.unite (staff);
- Real notey = note_ext[dir] - me->relative_coordinate (commony, Y_AXIS);
+ Interval tuplet_x (tuplets[i]->extent (commonx, X_AXIS));
+ Interval tuplet_y (tuplets[i]->extent (commony, Y_AXIS));
+
+ Direction d = LEFT;
+ Drul_array<Real> positions = ly_scm2realdrul (tuplets[i]->get_property ("positions"));
+
- Real x = columns[i]->relative_coordinate (commonx, X_AXIS) - x0;
- Real tuplety = *dy * x * factor;
+ Real other_dy = positions[RIGHT] - positions[LEFT];
- if (notey * dir > (*offset + tuplety) * dir)
- *offset = notey - tuplety;
+ do
+ {
+ Real y
+ = tuplet_y.linear_combination (d * sign (other_dy));
+
+#if 0
+ /*
+ Let's not 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 (Offset (tuplet_x[d] - x0, y));
+ }
+ while (flip (&d) != LEFT);
}
- // padding
- *offset += gh_scm2double (me->get_grob_property ("padding")) *dir;
+ Real factor = (columns.size () > 1) ? 1 / (x1 - x0) : 1.0;
+ for (int i = 0; i < points.size (); i++)
+ {
+ Real x = points[i][X_AXIS];
+ Real tuplety = (*dy) * x * factor;
+
+ if (points[i][Y_AXIS] * dir > (*offset + tuplety) * dir)
+ *offset = points[i][Y_AXIS] - tuplety;
+ }
+
+ *offset += scm_to_double (me->get_property ("padding")) * dir;
-
/*
horizontal brackets should not collide with staff lines.
-
- */
- Real ss= Staff_symbol_referencer::staff_space (me);
- if (*dy == 0 && fabs (*offset) < ss * Staff_symbol_referencer::staff_radius (me))
+
+ 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.
+ */
+ if (*dy == 0
+ && fabs (*offset) < ss * Staff_symbol_referencer::staff_radius (me))
{
// quantize, then do collision check.
*offset *= 2 / ss;
-
+
*offset = rint (*offset);
if (Staff_symbol_referencer::on_staffline (me, (int) rint (*offset)))
*offset += dir;
*offset *= 0.5 * ss;
}
-
-}
-
-/*
- use first -> last note for slope,
-*/
-void
-Tuplet_bracket::calc_dy (Grob*me,Real * dy)
-{
- Link_array<Grob> columns=
- Pointer_group_interface__extract_grobs (me, (Grob*)0, "note-columns");
-
- /*
- ugh. refps.
- */
- Direction d = get_grob_direction (me);
- *dy = columns.top ()->extent (columns.top (), Y_AXIS) [d]
- - columns[0]->extent (columns[0], Y_AXIS) [d];
}
-/*
- We depend on the beams if there are any.
- */
-MAKE_SCHEME_CALLBACK (Tuplet_bracket,before_line_breaking,1);
+MAKE_SCHEME_CALLBACK (Tuplet_bracket, calc_direction, 1);
SCM
-Tuplet_bracket::before_line_breaking (SCM smob)
+Tuplet_bracket::calc_direction (SCM smob)
{
Grob *me = unsmob_grob (smob);
- Link_array<Grob> columns=
- Pointer_group_interface__extract_grobs (me, (Grob*)0, "note-columns");
-
-
- for (int i = columns.size(); i--;)
- {
- Grob * s =Note_column::get_stem (columns[i]);
- Grob * b = s ? Stem::get_beam (s): 0;
- if (b)
- me->add_dependency (b);
- }
- return SCM_UNDEFINED;
+ Direction dir = Tuplet_bracket::get_default_dir (me);
+ return scm_from_int (dir);
}
-MAKE_SCHEME_CALLBACK (Tuplet_bracket,after_line_breaking,1);
-
+MAKE_SCHEME_CALLBACK (Tuplet_bracket, calc_positions, 1);
SCM
-Tuplet_bracket::after_line_breaking (SCM smob)
+Tuplet_bracket::calc_positions (SCM smob)
{
- Grob * me = unsmob_grob (smob);
- Link_array<Grob> columns=
- Pointer_group_interface__extract_grobs (me, (Grob*)0, "note-columns");
+ Grob *me = unsmob_grob (smob);
+ extract_grob_set (me, "note-columns", columns);
- if (!columns.size ())
- {
- me->suicide ();
- return SCM_UNSPECIFIED;
- }
- if (dynamic_cast<Spanner*> (me)->broken_b ())
- {
- me->warning (_("Killing tuplet bracket across linebreak."));
- me->suicide();
- return SCM_UNSPECIFIED;
- }
-
Direction dir = get_grob_direction (me);
- if (!dir)
- {
- dir = Tuplet_bracket::get_default_dir (me);
- set_grob_direction (me, dir);
- }
-
bool equally_long = false;
- Grob * par_beam = parallel_beam (me, columns, &equally_long);
+ Grob *par_beam = parallel_beam (me, columns, &equally_long);
/*
We follow the beam only if there is one, and we are next to it.
- */
- Real dy, offset;
+ */
+ Real dy = 0.0;
+ Real offset = 0.0;
if (!par_beam
|| get_grob_direction (par_beam) != dir)
- {
- calc_position_and_height (me,&offset,&dy);
- }
+ calc_position_and_height (me, &offset, &dy);
else
{
- SCM ps = par_beam->get_grob_property ("positions");
+ SCM ps = par_beam->get_property ("positions");
- Real lp = gh_scm2double (gh_car (ps));
- Real rp = gh_scm2double (gh_cdr (ps));
+ Real lp = scm_to_double (scm_car (ps));
+ Real rp = scm_to_double (scm_cdr (ps));
/*
duh. magic.
- */
- offset = lp + dir * (0.5 + gh_scm2double (me->get_grob_property ("padding")));
- dy = rp- lp;
+ */
+ offset = lp + dir * (0.5 + scm_to_double (me->get_property ("padding")));
+ dy = rp - lp;
}
+
+ SCM x = scm_cons (scm_from_double (offset),
+ scm_from_double (offset + dy));
- SCM lp = me->get_grob_property ("left-position");
- SCM rp = me->get_grob_property ("right-position");
-
- if (gh_number_p (lp) && !gh_number_p (rp))
- {
- rp = gh_double2scm (gh_scm2double (lp) + dy);
- }
- else if (gh_number_p (rp) && !gh_number_p (lp))
- {
- lp = gh_double2scm (gh_scm2double (rp) - dy);
- }
- else if (!gh_number_p (rp) && !gh_number_p (lp))
- {
- lp = gh_double2scm (offset);
- rp = gh_double2scm (offset +dy);
- }
-
- me->set_grob_property ("left-position", lp);
- me->set_grob_property ("right-position", rp);
-
- return SCM_UNSPECIFIED;
+ return x;
}
-
/*
- similar to slur.
- */
+ similar to beam ?
+*/
Direction
-Tuplet_bracket::get_default_dir (Grob*me)
+Tuplet_bracket::get_default_dir (Grob *me)
{
- Direction d = UP;
- for (SCM s = me->get_grob_property ("note-columns"); gh_pair_p (s); s = ly_cdr (s))
+ Drul_array<int> dirs (0, 0);
+ extract_grob_set (me, "note-columns", columns);
+ for (int i = 0; i < columns.size (); i++)
{
- Grob * nc = unsmob_grob (ly_car (s));
- if (Note_column::dir (nc) < 0)
- {
- d = DOWN;
- break;
- }
+ Grob *nc = columns[i];
+ Direction d = Note_column::dir (nc);
+ if (d)
+ dirs[d]++;
}
- return d;
+
+ return dirs[UP] >= dirs[DOWN] ? UP : DOWN;
}
void
-Tuplet_bracket::add_column (Grob*me, Item*n)
+Tuplet_bracket::add_column (Grob *me, Item *n)
{
Pointer_group_interface::add_grob (me, ly_symbol2scm ("note-columns"), n);
- me->add_dependency (n);
-
- add_bound_item (dynamic_cast<Spanner*> (me), n);
+ add_bound_item (dynamic_cast<Spanner *> (me), n);
}
+void
+Tuplet_bracket::add_tuplet_bracket (Grob *me, Grob *bracket)
+{
+ 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. ",
+
+
+ /* 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 ");
-
-
-ADD_INTERFACE (Tuplet_bracket,"tuplet-bracket-interface",
- "A bracket with a number in the middle, used for tuplets.",
- "note-columns bracket-flare edge-height shorten-pair padding gap left-position right-position bracket-visibility number-visibility thickness direction");
-