source file of the GNU LilyPond music typesetter
- (c) 1999--2005 Han-Wen Nienhuys <hanwen@cs.uu.nl>
+ (c) 1999--2007 Han-Wen Nienhuys <hanwen@xs4all.nl>
*/
-#include <math.h>
+#include "spacing-spanner.hh"
+#include <math.h>
#include <cstdio>
+
using namespace std;
-#include "spacing-spanner.hh"
-#include "paper-column.hh"
-#include "output-def.hh"
-#include "paper-score.hh"
-#include "system.hh"
+#include "spacing-options.hh"
+#include "international.hh"
+#include "main.hh"
#include "moment.hh"
#include "note-spacing.hh"
-#include "main.hh"
-#include "warn.hh"
+#include "output-def.hh"
+#include "paper-column.hh"
+#include "paper-score.hh"
#include "pointer-group-interface.hh"
#include "spaceable-grob.hh"
-#include "staff-spacing.hh"
#include "spacing-interface.hh"
+#include "staff-spacing.hh"
+#include "system.hh"
+#include "warn.hh"
-Rational
-Spacing_spanner::effective_shortest_duration (Grob *me,
- Link_array<Grob> const &all)
+vector<Grob*>
+Spacing_spanner::get_columns (Grob *me_grob)
{
- SCM preset_shortest = me->get_property ("common-shortest-duration");
- Rational global_shortest;
- if (unsmob_moment (preset_shortest))
- global_shortest = unsmob_moment (preset_shortest)->main_part_;
- else
- {
- global_shortest = Spacing_spanner::find_shortest (me, all);
- if (be_verbose_global)
- message (_f ("Global shortest duration is %s", global_shortest.to_string ()) + "\n");
- }
-
- return global_shortest;
+ Spanner *me = dynamic_cast<Spanner*> (me_grob);
+ vector<Grob*> all (get_root_system (me)->used_columns ());
+ vsize start = binary_search (all, (Grob*)me->get_bound (LEFT),
+ &Paper_column::less_than);
+ vsize end = binary_search (all, (Grob*) me->get_bound (RIGHT),
+ &Paper_column::less_than);
+
+ all = vector<Grob*>::vector<Grob*> (all.begin () + start,
+ all.begin () + end + 1);
+ return all;
}
-
MAKE_SCHEME_CALLBACK (Spacing_spanner, set_springs, 1);
SCM
Spacing_spanner::set_springs (SCM smob)
{
- Grob *me = unsmob_grob (smob);
+ Spanner *me = unsmob_spanner (smob);
/*
can't use get_system() ? --hwn.
*/
- Link_array<Grob> all (get_root_system (me)->columns ());
-
- set_explicit_neighbor_columns (all);
-
Spacing_options options;
options.init_from_grob (me);
- options.global_shortest_ = effective_shortest_duration (me, all);
+ vector<Grob*> cols = Spacing_spanner::get_columns (me);
+ set_explicit_neighbor_columns (cols);
- prune_loose_columns (me, &all, &options);
- set_implicit_neighbor_columns (all);
- generate_springs (me, all, &options);
+ prune_loose_columns (me, &cols, &options);
+ set_implicit_neighbor_columns (cols);
+ generate_springs (me, cols, &options);
return SCM_UNSPECIFIED;
}
note has a different duration, but hey, don't write that kind of
stuff, then.
*/
-Rational
-Spacing_spanner::find_shortest (Grob *me, Link_array<Grob> const &cols)
+
+MAKE_SCHEME_CALLBACK (Spacing_spanner, calc_common_shortest_duration, 1);
+SCM
+Spacing_spanner::calc_common_shortest_duration (SCM grob)
{
+ Spanner *me = unsmob_spanner (grob);
+
+ vector<Grob*> cols (get_columns (me));
+
/*
ascending in duration
*/
- Array<Rational> durations;
- Array<int> counts;
+ vector<Rational> durations;
+ vector<int> counts;
Rational shortest_in_measure;
shortest_in_measure.set_infinite (1);
- for (int i = 0; i < cols.size (); i++)
+ for (vsize i = 0; i < cols.size (); i++)
{
if (Paper_column::is_musical (cols[i]))
{
shortest_in_measure = min (shortest_in_measure, this_shortest.main_part_);
}
else if (!shortest_in_measure.is_infinity ()
- && Item::is_breakable (cols[i]))
+ && Paper_column::is_breakable (cols[i]))
{
- int j = 0;
+ vsize j = 0;
for (; j < durations.size (); j++)
{
if (durations[j] > shortest_in_measure)
{
- counts.insert (1, j);
- durations.insert (shortest_in_measure, j);
+ counts.insert (counts.begin () + j, 1);
+ durations.insert (durations.begin () + j, shortest_in_measure);
break;
}
else if (durations[j] == shortest_in_measure)
if (durations.size () == j)
{
- durations.push (shortest_in_measure);
- counts.push (1);
+ durations.push_back (shortest_in_measure);
+ counts.push_back (1);
}
shortest_in_measure.set_infinite (1);
int max_idx = -1;
int max_count = 0;
- for (int i = durations.size (); i--;)
+ for (vsize i = durations.size (); i--;)
{
if (counts[i] >= max_count)
{
max_idx = i;
max_count = counts[i];
}
-
- // printf ("duration %d/%d, count %d\n",
- // durations[i].num (), durations[i].den (), counts[i]);
}
SCM bsd = me->get_property ("base-shortest-duration");
if (max_idx >= 0)
d = min (d, durations[max_idx]);
- return d;
+ return Moment (d).smobbed_copy ();
}
void
void
Spacing_spanner::generate_springs (Grob *me,
- Link_array<Grob> const &cols,
+ vector<Grob*> const &cols,
Spacing_options const *options)
{
- Paper_column *next = 0;
- Paper_column *next_next = 0;
- for (int i = cols.size (); i--;)
+ Paper_column *prev = 0;
+ for (vsize i = 0; i < cols.size (); i++)
{
Paper_column *col = dynamic_cast<Paper_column *> (cols[i]);
- if (next)
- generate_pair_spacing (me, col, next, next_next, options);
+ Paper_column *next = (i + 1 < cols.size ()) ? dynamic_cast<Paper_column *> (cols[i+1]) : 0;
+
+ if (i > 0)
+ generate_pair_spacing (me, prev, col, next, options);
- next_next = next;
- next = col;
+ prev = col;
}
}
bool expand_only = false;
Real base_note_space = note_spacing (me, left_col, right_col, options, &expand_only);
+ Real max_fixed = 0;
+ Real max_space = 0;
Real compound_note_space = 0.0;
Real compound_fixed_note_space = 0.0;
if (options->stretch_uniformly_)
- compound_note_space = base_note_space;
+ {
+ compound_note_space = base_note_space;
+
+ if (!Paper_column::is_musical (right_col))
+ {
+ /*
+ Crude fix for notes that lead up to barlines and time sigs.
+ */
+ Interval lext = right_col->extent (right_col, X_AXIS);
+ if (!lext.is_empty ())
+ compound_note_space += -lext[LEFT];
+ }
+ }
else
{
int wish_count = 0;
-
+
extract_grob_set (left_col, "right-neighbors", neighbors);
/*
happens after the current note (this is set in the grob
property SPACING-SEQUENCE.
*/
- for (int i = 0; i < neighbors.size (); i++)
+ for (vsize i = 0; i < neighbors.size (); i++)
{
Grob *wish = neighbors[i];
Note_spacing::get_spacing (wish, right_col, base_note_space, options->increment_, &space, &fixed);
- compound_note_space = compound_note_space + space;
- compound_fixed_note_space = compound_fixed_note_space + fixed;
+
+ max_space = max (max_space, space);
+ max_fixed = max (max_fixed, fixed);
+
+ compound_note_space += space;
+ compound_fixed_note_space += fixed;
wish_count++;
}
}
if (compound_note_space < 0 || wish_count == 0)
{
- compound_note_space = base_note_space;
- compound_fixed_note_space = options->increment_;
+
+ if (!Paper_column::is_musical (right_col))
+ {
+ /*
+ reconsider this: breaks with wide marks/tempos/etc.
+ */
+ Real left_col_stick_out = robust_relative_extent (left_col, left_col, X_AXIS)[RIGHT];
+ compound_fixed_note_space = max (left_col_stick_out, options->increment_);
+
+ compound_note_space = max (base_note_space,
+ base_note_space - options->increment_ + left_col_stick_out);
+ }
+ else
+ {
+ /*
+ Fixed should be 0.0. If there are no spacing wishes, we're
+ likely dealing with polyphonic spacing of hemiolas.
+
+ We used to have compound_fixed_note_space = options->increment_
+
+ but this can lead to numeric instability problems when we
+ do
+
+ inverse_strength = (compound_note_space - compound_fixed_note_space)
+
+ */
+
+ compound_note_space = base_note_space;
+ compound_fixed_note_space = 0.0;
+ }
}
- else
+ else if (to_boolean (me->get_property ("average-spacing-wishes")))
{
compound_note_space /= wish_count;
compound_fixed_note_space /= wish_count;
}
+ else
+ {
+ compound_fixed_note_space = max_fixed;
+ compound_note_space = max_space;
+ }
/*
Whatever we do, the fixed space is smaller than the real
Spaceable_grob::add_spring (left_col, right_col, distance, inverse_strength);
}
+/*
+ Check if COL fills the whole measure.
+ */
+bool
+Spacing_spanner::fills_measure (Grob *me, Item *left, Item *col)
+{
+ System *sys = get_root_system (me);
+ Item *next = sys->column (col->get_column()->get_rank () + 1);
+ if (!next)
+ return false;
+
+ if (Paper_column::is_musical (next)
+ || Paper_column::is_musical (left)
+ || !Paper_column::is_musical (col)
+ || !Paper_column::is_used (next))
+ return false;
+
+ Moment dt =
+ Paper_column::when_mom (next) - Paper_column::when_mom (col);
+
+ Moment *len = unsmob_moment (left->get_property ("measure-length"));
+ if (!len)
+ return false;
+
+ /*
+ Don't check for exact measure length, since ending measures are
+ often shortened due to pickups.
+ */
+ if (dt.main_part_ > len->main_part_ / Rational (2)
+ && (next->is_broken ()
+ || next->break_status_dir ()))
+ return true;
+
+ return false;
+}
+
/*
Read hints from L and generate springs.
*/
{
Real compound_fixed = 0.0;
Real compound_space = 0.0;
+ Real max_fixed = 0.0;
+ Real max_space = 0.0;
+
int wish_count = 0;
Moment dt = Paper_column::when_mom (r) - Paper_column::when_mom (l);
{
extract_grob_set (l, "spacing-wishes", wishes);
- for (int i = 0; i < wishes.size (); i++)
+ for (vsize i = 0; i < wishes.size (); i++)
{
Item *spacing_grob = dynamic_cast<Item *> (wishes[i]);
if (!spacing_grob || !Staff_spacing::has_interface (spacing_grob))
continue;
- Real space;
- Real fixed_space;
+ Real space = 0.;
+ Real fixed_space = 0.;
/*
column for the left one settings should be ok due automatic
pointer munging.
-
*/
assert (spacing_grob->get_column () == l);
space *= 0.8;
}
+ max_space = max (max_space, space);
+ max_fixed = max (max_fixed, fixed_space);
+
compound_space += space;
compound_fixed += fixed_space;
wish_count++;
}
else
{
- compound_space /= wish_count;
- compound_fixed /= wish_count;
+ if (to_boolean (me->get_property ("average-spacing-wishes")))
+ {
+ compound_space /= wish_count;
+ compound_fixed /= wish_count;
+ }
+ else
+ {
+ compound_fixed = max_fixed;
+ compound_space = max_space;
+ }
+
}
+ if (Paper_column::is_musical (r)
+ && l->break_status_dir () == CENTER
+ && fills_measure (me, l, r))
+ {
+ compound_space += 1.0;
+ }
+
if (options->stretch_uniformly_ && l->break_status_dir () != RIGHT)
compound_fixed = 0.0;
assert (!isinf (compound_space));
compound_space = max (compound_space, compound_fixed);
- /*
- There used to be code that changed spacing depending on
- raggedright setting. Ugh.
-
- Do it more cleanly, or rename the property.
-
- */
Real inverse_strength = (compound_space - compound_fixed);
Real distance = compound_space;
Spaceable_grob::add_spring (l, r, distance, inverse_strength);
}
-ADD_INTERFACE (Spacing_spanner, "spacing-spanner-interface",
+ADD_INTERFACE (Spacing_spanner,
"The space taken by a note is dependent on its duration. Doubling a\n"
"duration adds spacing-increment to the space. The most common shortest\n"
"note gets @code{shortest-duration-space}. Notes that are even shorter are\n"
"head width) A 16th note is followed by 0.5 note head width. The\n"
"quarter note is followed by 3 NHW, the half by 4 NHW, etc.\n",
- "grace-space-factor "
- "spacing-increment "
+
+ "average-spacing-wishes "
"base-shortest-duration "
- "strict-note-spacing "
- "shortest-duration-space "
"common-shortest-duration "
- "uniform-stretching "
"packed-spacing "
+ "shortest-duration-space "
+ "spacing-increment "
+ "strict-grace-spacing "
+ "strict-note-spacing "
+ "uniform-stretching "
+
);
-ADD_INTERFACE (Spacing_interface, "spacing-interface",
- "Something to do with line breaking and spacing. "
- "Kill this one after determining line breaks.",
- "");
-