summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Solomon <mike@apollinemike.com>2011-03-16 15:07:34 -0400
committerMike Solomon <mike@apollinemike.com>2011-03-16 15:07:34 -0400
commita339f8b9865b0f02febd351ba494129d414fb568 (patch)
tree99a12240e9af206155e671356f104e3205966038
parent84bdcba4e72ed9161fa6091441a41a02d81069aa (diff)
Fixes Issue 1504, allowing feather beam line breaking.
Makes it such that the degree of feathering at the end of a system is preserved at the beginning of the next system. Adds a normalized-endpoints property to Spanner, which calculates the portion of a spanner (normalized from 0 to 1) taken up by any broken child.
-rw-r--r--input/regression/beam-feather-breaking.ly137
-rw-r--r--lily/beam.cc66
-rw-r--r--lily/include/spanner.hh1
-rw-r--r--lily/spanner.cc48
-rw-r--r--scm/define-grob-properties.scm3
-rw-r--r--scm/define-grobs.scm2
6 files changed, 247 insertions, 10 deletions
diff --git a/input/regression/beam-feather-breaking.ly b/input/regression/beam-feather-breaking.ly
new file mode 100644
index 0000000000..1b3aae08bf
--- /dev/null
+++ b/input/regression/beam-feather-breaking.ly
@@ -0,0 +1,137 @@
+\version "2.13.55"
+\header {
+ texidoc = "Feathered beams should have the same progress of their feathering
+at the end of a line break as they do at the beginning of the next line."
+}
+
+\paper {
+ left-margin = 2\cm
+ line-width = 10\cm
+ ragged-right = ##t
+ indent = 0\cm
+}
+
+\new Staff <<
+ \relative c' {
+ \cadenzaOn
+ \override Staff . TimeSignature #'stencil = ##f
+ \override Voice . Beam #'breakable = ##t
+ \once \override Voice . Beam #'grow-direction = #RIGHT
+ a32[ b c d e f g a ]
+ \once \override Voice . Beam #'grow-direction = #LEFT
+ a[ g f e d c b a] \bar "|"
+} >>
+
+\new Staff <<
+ \relative c' {
+ \cadenzaOn
+ \override Staff . TimeSignature #'stencil = ##f
+ \override Voice . Beam #'breakable = ##t
+ \once \override Voice . Beam #'grow-direction = #RIGHT
+ a32[ b c d \bar "" \break e f g a b c d e f g a ] \bar "|"
+} >>
+
+\new Staff <<
+ \relative c' {
+ \cadenzaOn
+ \override Staff . TimeSignature #'stencil = ##f
+ \override Voice . Beam #'breakable = ##t
+ \once \override Voice . Beam #'grow-direction = #RIGHT
+ a32[ b c d e f g a \bar "" \break b c d e f g a ] \bar "|"
+} >>
+
+\new Staff <<
+ \relative c' {
+ \cadenzaOn
+ \override Staff . TimeSignature #'stencil = ##f
+ \override Voice . Beam #'breakable = ##t
+ \once \override Voice . Beam #'grow-direction = #RIGHT
+ a32[ b c d e f g a b c d \bar "" \break e f g a ] \bar "|"
+} >>
+
+\new Staff <<
+ \relative c' {
+ \cadenzaOn
+ \override Staff . TimeSignature #'stencil = ##f
+ \override Voice . Beam #'breakable = ##t
+ \once \override Voice . Beam #'grow-direction = #LEFT
+ a32[ b c d \bar "" \break e f g a b c d e f g a ] \bar "|"
+} >>
+
+\new Staff <<
+ \relative c' {
+ \cadenzaOn
+ \override Staff . TimeSignature #'stencil = ##f
+ \override Voice . Beam #'breakable = ##t
+ \once \override Voice . Beam #'grow-direction = #LEFT
+ a32[ b c d e f g a \bar "" \break b c d e f g a ] \bar "|"
+} >>
+
+\new Staff <<
+ \relative c' {
+ \cadenzaOn
+ \override Staff . TimeSignature #'stencil = ##f
+ \override Voice . Beam #'breakable = ##t
+ \once \override Voice . Beam #'grow-direction = #LEFT
+ a32[ b c d e f g a b c d \bar "" \break e f g a ] \bar "|"
+} >>
+
+\new Staff <<
+ \relative c' {
+ \cadenzaOn
+ \override Staff . TimeSignature #'stencil = ##f
+ \override Voice . Stem #'direction = #DOWN
+ \override Voice . Beam #'breakable = ##t
+ \once \override Voice . Beam #'grow-direction = #RIGHT
+ a32[ b c d \bar "" \break e f g a b c d e f g a ] \bar "|"
+} >>
+
+\new Staff <<
+ \relative c' {
+ \cadenzaOn
+ \override Staff . TimeSignature #'stencil = ##f
+ \override Voice . Stem #'direction = #DOWN
+ \override Voice . Beam #'breakable = ##t
+ \once \override Voice . Beam #'grow-direction = #RIGHT
+ a32[ b c d e f g a \bar "" \break b c d e f g a ] \bar "|"
+} >>
+
+\new Staff <<
+ \relative c' {
+ \cadenzaOn
+ \override Staff . TimeSignature #'stencil = ##f
+ \override Voice . Stem #'direction = #DOWN
+ \override Voice . Beam #'breakable = ##t
+ \once \override Voice . Beam #'grow-direction = #RIGHT
+ a32[ b c d e f g a b c d \bar "" \break e f g a ] \bar "|"
+} >>
+
+\new Staff <<
+ \relative c' {
+ \cadenzaOn
+ \override Staff . TimeSignature #'stencil = ##f
+ \override Voice . Stem #'direction = #DOWN
+ \override Voice . Beam #'breakable = ##t
+ \once \override Voice . Beam #'grow-direction = #LEFT
+ a32[ b c d \bar "" \break e f g a b c d e f g a ] \bar "|"
+} >>
+
+\new Staff <<
+ \relative c' {
+ \cadenzaOn
+ \override Staff . TimeSignature #'stencil = ##f
+ \override Voice . Stem #'direction = #DOWN
+ \override Voice . Beam #'breakable = ##t
+ \once \override Voice . Beam #'grow-direction = #LEFT
+ a32[ b c d e f g a \bar "" \break b c d e f g a ] \bar "|"
+} >>
+
+\new Staff <<
+ \relative c' {
+ \cadenzaOn
+ \override Staff . TimeSignature #'stencil = ##f
+ \override Voice . Stem #'direction = #DOWN
+ \override Voice . Beam #'breakable = ##t
+ \once \override Voice . Beam #'grow-direction = #LEFT
+ a32[ b c d e f g a b c d \bar "" \break e f g a ] \bar "|"
+} >>
diff --git a/lily/beam.cc b/lily/beam.cc
index 53fda81743..e3135dadfa 100644
--- a/lily/beam.cc
+++ b/lily/beam.cc
@@ -590,24 +590,72 @@ Beam::print (SCM grob)
Direction feather_dir = to_dir (me->get_property ("grow-direction"));
+ Interval placements = robust_scm2interval (me->get_property ("normalized-endpoints"), Interval (0.0, 0.0));
+
Stencil the_beam;
+
+ int extreme = (segments[0].vertical_count_ == 0
+ ? segments[0].vertical_count_
+ : segments.back ().vertical_count_);
+
for (vsize i = 0; i < segments.size (); i ++)
{
Real local_slope = slope;
+ /*
+ Makes local slope proportional to the ratio of the length of this beam
+ to the total length.
+ */
if (feather_dir)
- {
- local_slope += feather_dir * segments[i].vertical_count_ * beam_dy / span.length ();
- }
+ local_slope += (feather_dir * segments[i].vertical_count_
+ * beam_dy
+ * placements.length ()
+ / span.length ());
Stencil b = Lookup::beam (local_slope, segments[i].horizontal_.length (), beam_thickness, blot);
b.translate_axis (segments[i].horizontal_[LEFT], X_AXIS);
+ Real multiplier = feather_dir ? placements[LEFT] : 1.0;
+
+ Interval weights (1 - multiplier, multiplier);
+
+ if (feather_dir != LEFT)
+ weights.swap ();
+
+ // we need two translations: the normal one and
+ // the one of the lowest segment
+ int idx[] = {i, extreme};
+ Real translations[2];
+
+ for (int j = 0; j < 2; j++)
+ translations[j] = slope
+ * (segments[idx[j]].horizontal_[LEFT] - span.linear_combination (CENTER))
+ + pos.linear_combination (CENTER)
+ + beam_dy * segments[idx[j]].vertical_count_;
+
+ Real weighted_average = translations[0] * weights[LEFT] + translations[1] * weights[RIGHT];
+
+ /*
+ Tricky. The manipulation of the variable `weighted_average' below ensures
+ that beams with a RIGHT grow direction will start from the position of the
+ lowest segment at 0, and this error will decrease and decrease over the
+ course of the beam. Something with a LEFT grow direction, on the other
+ hand, will always start in the correct place but progressively accrue
+ error at broken places. This code shifts beams up given where they are
+ in the total span length (controlled by the variable `multiplier'). To
+ better understand what it does, try commenting it out: you'll see that
+ all of the RIGHT growing beams immediately start too low and get better
+ over line breaks, whereas all of the LEFT growing beams start just right
+ and get worse over line breaks.
+ */
+ Real factor = Interval (multiplier, 1 - multiplier).linear_combination (feather_dir);
+
+ if (segments[0].vertical_count_ < 0 && feather_dir)
+ weighted_average += beam_dy * (segments.size () - 1) * factor;
+
+ b.translate_axis (weighted_average, Y_AXIS);
- b.translate_axis (local_slope
- * (segments[i].horizontal_[LEFT] - span.linear_combination (feather_dir))
- + pos.linear_combination (feather_dir)
- + beam_dy * segments[i].vertical_count_, Y_AXIS);
the_beam.add_stencil (b);
+
}
#if (DEBUG_BEAM_SCORING)
@@ -626,7 +674,7 @@ Beam::print (SCM grob)
properties = scm_cons(scm_acons (ly_symbol2scm ("font-size"), scm_from_int (-5), SCM_EOL),
properties);
-
+
Direction stem_dir = stems.size () ? to_dir (stems[0]->get_property ("direction")) : UP;
Stencil score = *unsmob_stencil (Text_interface::interpret_markup
@@ -1088,7 +1136,7 @@ Beam::shift_region_to_valid (SCM grob, SCM posns)
Real x = s->relative_coordinate (common[X_AXIS], X_AXIS) - x_span[LEFT];
x_posns.push_back (x);
}
-
+
Grob *lvs = last_normal_stem (me);
x_span[RIGHT] = lvs->relative_coordinate (common[X_AXIS], X_AXIS);
diff --git a/lily/include/spanner.hh b/lily/include/spanner.hh
index e0f461a7db..7bda060c86 100644
--- a/lily/include/spanner.hh
+++ b/lily/include/spanner.hh
@@ -48,6 +48,7 @@ class Spanner : public Grob
public:
DECLARE_SCHEME_CALLBACK (set_spacing_rods, (SCM));
+ DECLARE_SCHEME_CALLBACK (calc_normalized_endpoints, (SCM));
DECLARE_SCHEME_CALLBACK (bounds_width, (SCM));
DECLARE_SCHEME_CALLBACK (kill_zero_spanned_time, (SCM));
diff --git a/lily/spanner.cc b/lily/spanner.cc
index 6083c67461..d3fd5a0955 100644
--- a/lily/spanner.cc
+++ b/lily/spanner.cc
@@ -398,6 +398,53 @@ Spanner::set_spacing_rods (SCM smob)
return SCM_UNSPECIFIED;
}
+MAKE_SCHEME_CALLBACK (Spanner, calc_normalized_endpoints, 1);
+SCM
+Spanner::calc_normalized_endpoints (SCM smob)
+{
+ Spanner *me = unsmob_spanner (smob);
+ SCM result = SCM_EOL;
+
+ Spanner *orig = dynamic_cast<Spanner *> (me->original ());
+
+ orig = orig ? orig : me;
+
+ if (orig->is_broken ())
+ {
+ Real total_width = 0.0;
+ vector<Real> span_data;
+
+ if (!orig->is_broken ())
+ span_data.push_back (orig->spanner_length ());
+ else
+ for (vsize i = 0; i < orig->broken_intos_.size (); i++)
+ span_data.push_back (orig->broken_intos_[i]->spanner_length ());
+
+ vector<Interval> unnormalized_endpoints;
+
+ for (vsize i = 0; i < span_data.size (); i++)
+ {
+ unnormalized_endpoints.push_back (Interval (total_width, total_width + span_data[i]));
+ total_width += span_data[i];
+ }
+
+ for (vsize i = 0; i < unnormalized_endpoints.size (); i++)
+ {
+ SCM t = ly_interval2scm (1 / total_width * unnormalized_endpoints[i]);
+ orig->broken_intos_[i]->set_property ("normalized-endpoints", t);
+ if (me->get_break_index () == i)
+ result = t;
+ }
+ }
+ else
+ {
+ result = scm_cons (scm_from_double (0.0), scm_from_double (1.0));
+ orig->set_property ("normalized-endpoints", result);
+ }
+
+ return result;
+}
+
Spanner *
unsmob_spanner (SCM s)
{
@@ -479,6 +526,7 @@ ADD_INTERFACE (Spanner,
" point of the spanner.",
/* properties */
+ "normalized-endpoints "
"minimum-length "
"to-barline "
);
diff --git a/scm/define-grob-properties.scm b/scm/define-grob-properties.scm
index 879a1c780d..83b327413d 100644
--- a/scm/define-grob-properties.scm
+++ b/scm/define-grob-properties.scm
@@ -616,6 +616,9 @@ the nearest staff in the opposite direction from
between the two, and @code{staff-affinity} is either @code{UP} or
@code{DOWN}. See @code{staff-staff-spacing} for a description of
the alist structure.")
+ (normalized-endpoints ,pair? "Represents left and right placement
+over the total spanner, where the width of the spanner is normalized
+between 0 and 1.")
(note-names ,vector? "Vector of strings containing names for
easy-notation note heads.")
diff --git a/scm/define-grobs.scm b/scm/define-grobs.scm
index 6e4faedf40..8b9564f41e 100644
--- a/scm/define-grobs.scm
+++ b/scm/define-grobs.scm
@@ -326,7 +326,6 @@
. (
;; todo: clean this up a bit: the list is getting
;; rather long.
-
(auto-knee-gap . 5.5)
(beam-thickness . 0.48) ; in staff-space
@@ -367,6 +366,7 @@
(round-to-zero-slope . 0.02)))
(direction . ,ly:beam::calc-direction)
+ (normalized-endpoints . ,ly:spanner::calc-normalized-endpoints)
;; only for debugging.
(font-family . roman)