diff options
author | Han-Wen Nienhuys <hanwen@xs4all.nl> | 2005-08-04 01:01:13 +0000 |
---|---|---|
committer | Han-Wen Nienhuys <hanwen@xs4all.nl> | 2005-08-04 01:01:13 +0000 |
commit | 3db73bcf98d9ac3ccfe33e08ff3cd0292bf65d06 (patch) | |
tree | 6ea073b7cbbd716d4d58e1a2f9c7934d3a0f1e9a | |
parent | f09878d50f77b732964fcda8827abde5e44180ce (diff) |
o * lily/spacing-engraver.cc (stop_translation_timestep): directly
copy proportionalNotationDuration into currentMusicalColumn if
set.
* lily/spacing-spanner.cc (musical_column_spacing): if
uniform-stretching set, fixed space is 0.0
* input/regression/spacing-multi-tuplet.ly: show uniform-stretching property.
* lily/spacing-spanner.cc (effective_shortest_duration): new function.
* lily/include/spacing-spanner.hh (class Spacing_spanner): new file.
(struct Spacing_options): new struct.
* lily/spacing-basic.cc: new file.
* lily/spacing-spanner.cc (note_spacing): remove outdated code:
delta_t doesn't have to be smaller than shortest_playing_len
-rw-r--r-- | ChangeLog | 21 | ||||
-rw-r--r-- | Documentation/misc/ChangeLog-1.5 | 2 | ||||
-rw-r--r-- | input/regression/chord-tremolo.ly | 47 | ||||
-rw-r--r-- | input/regression/markup-arrows.ly | 26 | ||||
-rw-r--r-- | input/regression/spacing-multi-tuplet.ly | 56 | ||||
-rw-r--r-- | input/regression/tuplet-broken.ly | 11 | ||||
-rw-r--r-- | lily/chord-tremolo-iterator.cc | 5 | ||||
-rw-r--r-- | lily/include/spacing-spanner.hh | 47 | ||||
-rw-r--r-- | lily/spacing-basic.cc | 198 | ||||
-rw-r--r-- | lily/spacing-engraver.cc | 24 | ||||
-rw-r--r-- | lily/spacing-loose-columns.cc | 320 | ||||
-rw-r--r-- | lily/spacing-spanner.cc | 701 | ||||
-rw-r--r-- | mf/feta-arrow.mf | 3 | ||||
-rw-r--r-- | scm/define-grob-properties.scm | 36 | ||||
-rw-r--r-- | scm/framework-gnome.scm | 4 | ||||
-rw-r--r-- | scm/lily.scm | 12 |
16 files changed, 816 insertions, 697 deletions
@@ -1,3 +1,24 @@ +2005-08-04 Han-Wen Nienhuys <hanwen@xs4all.nl> + +o * lily/spacing-engraver.cc (stop_translation_timestep): directly + copy proportionalNotationDuration into currentMusicalColumn if + set. + + * lily/spacing-spanner.cc (musical_column_spacing): if + uniform-stretching set, fixed space is 0.0 + + * input/regression/spacing-multi-tuplet.ly: show uniform-stretching property. + + * lily/spacing-spanner.cc (effective_shortest_duration): new function. + + * lily/include/spacing-spanner.hh (class Spacing_spanner): new file. + (struct Spacing_options): new struct. + + * lily/spacing-basic.cc: new file. + + * lily/spacing-spanner.cc (note_spacing): remove outdated code: + delta_t doesn't have to be smaller than shortest_playing_len + 2005-08-03 Han-Wen Nienhuys <hanwen@xs4all.nl> * input/regression/markup-arrows.ly: new file. diff --git a/Documentation/misc/ChangeLog-1.5 b/Documentation/misc/ChangeLog-1.5 index 3ac14a47f7..c65788f4ef 100644 --- a/Documentation/misc/ChangeLog-1.5 +++ b/Documentation/misc/ChangeLog-1.5 @@ -1,4 +1,4 @@ - 2002-08-19 Han-Wen Nienhuys <hanwen@cs.uu.nl> +2002-08-19 Han-Wen Nienhuys <hanwen@cs.uu.nl> * VERSION: release 1.6.0 diff --git a/input/regression/chord-tremolo.ly b/input/regression/chord-tremolo.ly index fe2c912a0b..734ec8e200 100644 --- a/input/regression/chord-tremolo.ly +++ b/input/regression/chord-tremolo.ly @@ -16,31 +16,26 @@ notes as well.) " } -\score { - \context Voice \relative c' { - \time 4/4 - \repeat "tremolo" 16 { d32 e } - \repeat "tremolo" 8 { d16 e } - \repeat "tremolo" 4 { d8 e } - - \time 3/4 - \repeat "tremolo" 12 { d32 e } - \repeat "tremolo" 6 { d16 e } - \repeat "tremolo" 3 { d8 e } - - \time 2/4 - \repeat "tremolo" 8 { d32 e } - \repeat "tremolo" 4 { d16 e } - \repeat "tremolo" 2 { d8 e } - - \time 1/4 - \repeat "tremolo" 4 { d32 e } - \repeat "tremolo" 2 { d16 e } - - c4 c4 c4 c4 c4 - } - - \midi { } - \layout {} +\context Voice \relative c' { + \time 4/4 + \repeat "tremolo" 16 { d32 e } + \repeat "tremolo" 8 { d16 e } + \repeat "tremolo" 4 { d8 e } + + \time 3/4 + \repeat "tremolo" 12 { d32 e } + \repeat "tremolo" 6 { d16 e } + \repeat "tremolo" 3 { d8 e } + + \time 2/4 + \repeat "tremolo" 8 { d32 e } + \repeat "tremolo" 4 { d16 e } + \repeat "tremolo" 2 { d8 e } + + \time 1/4 + \repeat "tremolo" 4 { d32 e } + \repeat "tremolo" 2 { d16 e } + + c4 c4 c4 c4 c4 } diff --git a/input/regression/markup-arrows.ly b/input/regression/markup-arrows.ly index 51a4c37004..8f822935b5 100644 --- a/input/regression/markup-arrows.ly +++ b/input/regression/markup-arrows.ly @@ -3,22 +3,26 @@ texidoc = "The feta font has arrow heads" } - \lyrics { \markup { - filled - - \arrow-head #0 #1 ##t - \arrow-head #0 #-1 ##t - \arrow-head #1 #1 ##t - \arrow-head #1 #-1 ##t + \arrow-head #X #RIGHT ##t + " " + \arrow-head #X #LEFT ##t + " " + \arrow-head #Y #RIGHT ##t + " " + \arrow-head #Y #LEFT ##t + " " - \arrow-head #0 #1 ##f - \arrow-head #0 #-1 ##f - \arrow-head #1 #1 ##f - \arrow-head #1 #-1 ##f + \arrow-head #X #RIGHT ##f + " " + \arrow-head #X #LEFT ##f + " " + \arrow-head #Y #RIGHT ##f + " " + \arrow-head #Y #LEFT ##f } } diff --git a/input/regression/spacing-multi-tuplet.ly b/input/regression/spacing-multi-tuplet.ly index baa8752bd4..17a45604fd 100644 --- a/input/regression/spacing-multi-tuplet.ly +++ b/input/regression/spacing-multi-tuplet.ly @@ -1,34 +1,32 @@ -\version "2.6.0" -\header{ -texidoc = "Concurrent tuplets should be equidistant on all staffs. - -Note that this only spaces correctly (exactly) when @code{raggedright} -is enabled. For a non-@code{raggedright} case, it still shows a bug: -uneven spacing. -" } - - - -multipart = \relative c'{ - \context StaffGroup << - \new Staff \context Voice { - \times 2/10 { c8[ c c c c c c c c c] } - \times 2/10 { c[ c c c c c c c c c] } - } - \new Staff \context Voice { - \times 2/11 { c8[ c c c c c c c c c c] } - \times 2/11 { c[ c c c c c c c c c c] } - } - >> - } - -\score{ - { - \multipart - } +\version "2.6.0" \header{ + -% \layout { raggedright = ##t } + texidoc = "Concurrent tuplets should be equidistant on all staffs. +Such equidistant spacing is it at odds with elegant engraver spacing; +hence it must be switched on explicitly with the +@code{uniform-stretching} property of @code{SpacingSpanner}. +" } + +\relative c' { + \new Score \with + { + \override SpacingSpanner #'uniform-stretching = ##t + } + \context StaffGroup << + \new Staff \context Voice { + \times 2/10 { c8[ c c c c c c c c c] } + \times 2/10 { c[ c c c c c c c c c] } + } + \new Staff \context Voice { + \times 2/11 { c8[ c c c c c c c c c c] } + \times 2/11 { c[ c c c c c c c c c c] } + } + >> +} + + + diff --git a/input/regression/tuplet-broken.ly b/input/regression/tuplet-broken.ly index 43f7ca4e15..87a17f8f35 100644 --- a/input/regression/tuplet-broken.ly +++ b/input/regression/tuplet-broken.ly @@ -1,8 +1,13 @@ \header { - texidoc = "Broken tuplets are adorned with little arrows." + + texidoc = "Broken tuplets are adorned with little arrows. The arrows + come from the @code{edge-text} property, and thus be replaced with + larger glyphs or other text. " + } + \version "2.7.4" \paper { @@ -16,8 +21,8 @@ \override TupletBracket #'edge-text = #(cons (markup #:fontsize 6 - #:arrow-head 0 -1 #f) - (markup #:arrow-head 0 1 #f)) + #:arrow-head Y LEFT #f) + (markup #:arrow-head X RIGHT #f)) \times 11/19 { c4 c4 c4 c4 \bar "empty" \break diff --git a/lily/chord-tremolo-iterator.cc b/lily/chord-tremolo-iterator.cc index 7301066d5b..b7d3bc826e 100644 --- a/lily/chord-tremolo-iterator.cc +++ b/lily/chord-tremolo-iterator.cc @@ -6,11 +6,6 @@ (c) 2000--2005 Han-Wen Nienhuys <hanwen@cs.uu.nl> */ -/* - this is culled from various other iterators, but sharing code by - subclassing proved to be too difficult. -*/ - #include "chord-tremolo-iterator.hh" #include "input.hh" diff --git a/lily/include/spacing-spanner.hh b/lily/include/spacing-spanner.hh new file mode 100644 index 0000000000..effb83920f --- /dev/null +++ b/lily/include/spacing-spanner.hh @@ -0,0 +1,47 @@ +/* + spacing-spanner.hh -- declare Spacing spanner + + source file of the GNU LilyPond music typesetter + + (c) 2005 Han-Wen Nienhuys <hanwen@xs4all.nl> + +*/ + +#ifndef SPACING_SPANNER_HH +#define SPACING_SPANNER_HH + +#include "lily-proto.hh" +#include "lily-guile.hh" +#include "rational.hh" + +struct Spacing_options +{ + bool packed_; + bool uniform_; + Rational global_shortest_; + + void init (Grob *me); +}; + +class Spacing_spanner +{ +public: + static void standard_breakable_column_spacing (Grob *me, Item *l, Item *r, + Real *fixed, Real *space, Spacing_options const*); + + static Real default_bar_spacing (Grob *, Grob *, Grob *, Moment); + static Real note_spacing (Grob *, Grob *, Grob *, Spacing_options const*, bool *); + static Real get_duration_space (Grob *, Moment dur, Rational shortest, bool *); + static Rational find_shortest (Grob *, Link_array<Grob> const &); + static Rational effective_shortest_duration (Grob *me, Link_array<Grob> const &all); + static void breakable_column_spacing (Grob *, Item *l, Item *r, Spacing_options const*); + static void prune_loose_columns (Grob *, Link_array<Grob> *cols, Spacing_options const*); + static void set_explicit_neighbor_columns (Link_array<Grob> const &cols); + static void set_implicit_neighbor_columns (Link_array<Grob> const &cols); + static void do_measure (Grob *me, Link_array<Grob> *cols, Spacing_options const*); + static void musical_column_spacing (Grob *, Item *, Item *, Real, Spacing_options const*); + DECLARE_SCHEME_CALLBACK (set_springs, (SCM)); + static bool has_interface (Grob *); +}; + +#endif /* SPACING_SPANNER_HH */ diff --git a/lily/spacing-basic.cc b/lily/spacing-basic.cc new file mode 100644 index 0000000000..7f4a4930b0 --- /dev/null +++ b/lily/spacing-basic.cc @@ -0,0 +1,198 @@ +/* + spacing-basic.cc -- implement Spacing_spanner, simplistic spacing routines + + source file of the GNU LilyPond music typesetter + + (c) 2005 Han-Wen Nienhuys <hanwen@xs4all.nl> + +*/ + +#include "spacing-spanner.hh" +#include "moment.hh" +#include "paper-column.hh" +#include "misc.hh" +#include "warn.hh" + +/* + LilyPond spaces by taking a simple-minded spacing algorithm, and + adding subtle adjustments to that. This file does the simple-minded + spacing routines. +*/ + + +/* + Get the measure wide ant for arithmetic spacing. +*/ +Real +Spacing_spanner::get_duration_space (Grob *me, + Moment d, + Rational shortest, bool *expand_only) +{ + Real k = robust_scm2double (me->get_property ("shortest-duration-space"), 1); + Real incr = robust_scm2double (me->get_property ("spacing-increment"), 1); + + if (d < shortest) + { + /* + We don't space really short notes using the log of the + duration, since it would disproportionally stretches the long + notes in a piece. In stead, we use geometric spacing with constant 0.5 + (i.e. linear.) + + This should probably be tunable, to use other base numbers. + + In Mozart hrn3 by EB., we have 8th note = 3.9 mm (total), 16th note = + 3.6 mm (total). head-width = 2.4, so we 1.2mm for 16th, 1.5 + mm for 8th. (white space), suggesting that we use + + (1.2 / 1.5)^{-log2(duration ratio)} + + + */ + Rational ratio = d.main_part_ / shortest; + + return ((k - 1) + double (ratio)) * incr; + } + else + { + /* + John S. Gourlay. ``Spacing a Line of Music, '' Technical + Report OSU-CISRC-10/87-TR35, Department of Computer and + Information Science, The Ohio State University, 1987. + */ + Real log = log_2 (shortest); + k -= log; + Rational compdur = d.main_part_ + d.grace_part_ / Rational (3); + *expand_only = false; + + return (log_2 (compdur) + k) * incr; + } +} + + +/* + The one-size-fits all spacing. It doesn't take into account + different spacing wishes from one to the next column. +*/ +void +Spacing_spanner::standard_breakable_column_spacing (Grob *me, Item *l, Item *r, + Real *fixed, Real *space, + Spacing_options const *options) +{ + *fixed = 0.0; + Direction d = LEFT; + Drul_array<Item *> cols (l, r); + + do + { + if (!Paper_column::is_musical (cols[d])) + { + /* + Tied accidentals over barlines cause problems, so lets see + what happens if we do this for non musical columns only. + */ + Interval lext = cols[d]->extent (cols [d], X_AXIS); + if (!lext.is_empty ()) + *fixed += -d * lext[-d]; + } + } + while (flip (&d) != LEFT); + + if (l->is_breakable (l) && r->is_breakable (r)) + { + Moment *dt = unsmob_moment (l->get_property ("measure-length")); + Moment mlen (1); + if (dt) + mlen = *dt; + + Real incr = robust_scm2double (me->get_property ("spacing-increment"), 1); + + *space = *fixed + incr * double (mlen.main_part_ / options->global_shortest_) * 0.8; + } + else + { + Moment dt = Paper_column::when_mom (r) - Paper_column::when_mom (l); + + if (dt == Moment (0, 0)) + { + /* + In this case, Staff_spacing should handle the job, + using dt when it is 0 is silly. + */ + *space = *fixed + 0.5; + } + else + { + bool dummy; + *space = *fixed + get_duration_space (me, dt, options->global_shortest_, &dummy); + } + } +} + +Real +Spacing_spanner::note_spacing (Grob *me, Grob *lc, Grob *rc, + Spacing_options const *options , + bool *expand_only) +{ + Moment shortest_playing_len = 0; + SCM s = lc->get_property ("shortest-playing-duration"); + + if (unsmob_moment (s)) + shortest_playing_len = *unsmob_moment (s); + + if (! shortest_playing_len.to_bool ()) + { + programming_error ("can't find a ruling note at " + Paper_column::when_mom (lc).to_string ()); + shortest_playing_len = 1; + } + + Moment lwhen = Paper_column::when_mom (lc); + Moment rwhen = Paper_column::when_mom (rc); + + Moment delta_t = rwhen - lwhen; + if (!Paper_column::is_musical (rc)) + { + /* + when toying with mmrests, it is possible to have musical + column on the left and non-musical on the right, spanning + several measures. + */ + + Moment *dt = unsmob_moment (rc->get_property ("measure-length")); + if (dt) + { + delta_t = min (delta_t, *dt); + + /* + The following is an extra safety measure, such that + the length of a mmrest event doesn't cause havoc. + */ + shortest_playing_len = min (shortest_playing_len, *dt); + } + } + + Real dist = 0.0; + if (delta_t.main_part_ && !lwhen.grace_part_) + { + dist = get_duration_space (me, shortest_playing_len, + options->global_shortest_, expand_only); + dist *= double (delta_t.main_part_ / shortest_playing_len.main_part_); + } + else if (delta_t.grace_part_) + { + /* + Crude hack for spacing graces: we take the shortest space + available (namely the space for the global shortest note), and + multiply that by grace-space-factor + */ + dist = get_duration_space (me, options->global_shortest_, + options->global_shortest_, expand_only); + + Real grace_fact + = robust_scm2double (me->get_property ("grace-space-factor"), 1); + + dist *= grace_fact; + } + + return dist; +} diff --git a/lily/spacing-engraver.cc b/lily/spacing-engraver.cc index 8bf477d355..d74991cfd2 100644 --- a/lily/spacing-engraver.cc +++ b/lily/spacing-engraver.cc @@ -45,10 +45,12 @@ class Spacing_engraver : public Engraver Spanner *spacing_; TRANSLATOR_DECLARATIONS (Spacing_engraver); + protected: DECLARE_ACKNOWLEDGER (staff_spacing); DECLARE_ACKNOWLEDGER (note_spacing); DECLARE_ACKNOWLEDGER (rhythmic_head); + void start_translation_timestep (); void stop_translation_timestep (); void process_music (); @@ -132,6 +134,18 @@ Spacing_engraver::acknowledge_rhythmic_head (Grob_info i) void Spacing_engraver::stop_translation_timestep () { + Paper_column *musical_column + = dynamic_cast<Paper_column *> (unsmob_grob (get_property ("currentMusicalColumn"))); + + SCM proportional = get_property ("proportionalNotationDuration"); + if (unsmob_moment (proportional)) + { + musical_column->set_property ("shortest-playing-duration", proportional); + musical_column->set_property ("shortest-starter-duration", proportional); + return; + } + + Moment shortest_playing; shortest_playing.set_infinite (1); for (int i = 0; i < playing_durations_.size (); i++) @@ -159,15 +173,13 @@ Spacing_engraver::stop_translation_timestep () shortest_playing = min (shortest_playing, starter); - Paper_column *sc - = dynamic_cast<Paper_column *> (unsmob_grob (get_property ("currentMusicalColumn"))); - + assert (starter.to_bool ()); SCM sh = shortest_playing.smobbed_copy (); SCM st = starter.smobbed_copy (); - sc->set_property ("shortest-playing-duration", sh); - sc->set_property ("shortest-starter-duration", st); + musical_column->set_property ("shortest-playing-duration", sh); + musical_column->set_property ("shortest-starter-duration", st); } void @@ -191,5 +203,5 @@ ADD_TRANSLATOR (Spacing_engraver, /* descr */ "make a SpacingSpanner and do bookkeeping of shortest starting and playing notes ", /* creats*/ "SpacingSpanner", /* accepts */ "", - /* reads */ "", + /* reads */ "currentMusicalColumn currentCommandColumn proportionalNotationDuration", /* write */ ""); diff --git a/lily/spacing-loose-columns.cc b/lily/spacing-loose-columns.cc index ab28747734..c50ee887d9 100644 --- a/lily/spacing-loose-columns.cc +++ b/lily/spacing-loose-columns.cc @@ -1,5 +1,5 @@ /* - spacing-loose-columns.cc -- implement loose column spacing. + spacing-loose-columns.cc -- implement loose column spacing. source file of the GNU LilyPond music typesetter @@ -12,6 +12,10 @@ #include "column-x-positions.hh" #include "staff-spacing.hh" #include "pointer-group-interface.hh" +#include "spacing-spanner.hh" +#include "note-spacing.hh" + +#include "break-align-interface.hh" /* Find the loose columns in POSNS, and drape them around the columns specified in BETWEEN-COLS. */ @@ -102,3 +106,317 @@ set_loose_columns (System *which, Column_x_positions const *posns) } } + + +/* + Return whether COL is fixed to its neighbors by some kind of spacing + constraint. + + + If in doubt, then we're not loose; the spacing engine should space + for it, risking suboptimal spacing. + + (Otherwise, we might risk core dumps, and other weird stuff.) +*/ +static bool +loose_column (Grob *l, Grob *c, Grob *r) +{ + extract_grob_set (c, "right-neighbors", rns); + extract_grob_set (c, "left-neighbors", lns); + + /* + If this column doesn't have a proper neighbor, we should really + make it loose, but spacing it correctly is more than we can + currently can handle. + + (this happens in the following situation: + + | + | clef G + * + + | | || + | | || + O O || + + + the column containing the clef is really loose, and should be + attached right to the first column, but that is a lot of work for + such a borderline case.) + + */ + if (lns.is_empty () || rns.is_empty ()) + return false; + + Item *l_neighbor = dynamic_cast<Item *> (lns[0]); + Item *r_neighbor = dynamic_cast<Item *> (rns[0]); + + if (!l_neighbor || !r_neighbor) + return false; + + l_neighbor = l_neighbor->get_column (); + r_neighbor = dynamic_cast<Item *> (Note_spacing::right_column (r_neighbor)); + + if (l == l_neighbor && r == r_neighbor) + return false; + + if (!l_neighbor || !r_neighbor) + return false; + + /* + Only declare loose if the bounds make a little sense. This means + some cases (two isolated, consecutive clef changes) won't be + nicely folded, but hey, then don't do that. + */ + if (! ((Paper_column::is_musical (l_neighbor) || Item::is_breakable (l_neighbor)) + && (Paper_column::is_musical (r_neighbor) || Item::is_breakable (r_neighbor)))) + { + return false; + } + + /* + A rather hairy check, but we really only want to move around + clefs. (anything else?) + + in any case, we don't want to move bar lines. + */ + extract_grob_set (c, "elements", elts); + for (int i = elts.size (); i--; ) + { + Grob *g = elts[i]; + if (g && Break_align_interface::has_interface (g)) + { + extract_grob_set (g, "elements", gelts); + for (int j = gelts.size (); j--; ) + { + Grob *h = gelts[j]; + + /* + ugh. -- fix staff-bar name? + */ + if (h && h->get_property ("break-align-symbol") == ly_symbol2scm ("staff-bar")) + return false; + } + } + } + + return true; +} + +/* + Remove columns that are not tightly fitting from COLS. In the + removed columns, set 'between-cols to the columns where it is in + between. +*/ +void +Spacing_spanner::prune_loose_columns (Grob *me, Link_array<Grob> *cols, + Spacing_options const *options) +{ + Link_array<Grob> newcols; + Real increment = robust_scm2double (me->get_property ("spacing-increment"), 1.2); + for (int i = 0; i < cols->size (); i++) + { + if (Item::is_breakable (cols->elem (i)) + || Paper_column::is_musical (cols->elem (i))) + { + newcols.push (cols->elem (i)); + continue; + } + + Grob *c = cols->elem (i); + if (loose_column (cols->elem (i - 1), c, cols->elem (i + 1))) + { + extract_grob_set (c, "right-neighbors", rns_arr); + extract_grob_set (c, "left-neighbors", lns_arr); + + SCM lns = lns_arr.size () ? lns_arr.top()->self_scm () : SCM_BOOL_F; + SCM rns = rns_arr.size () ? rns_arr.top()->self_scm () : SCM_BOOL_F; + + /* + Either object can be non existent, if the score ends + prematurely. + */ + + extract_grob_set (unsmob_grob (rns), "right-items", right_items); + c->set_object ("between-cols", scm_cons (lns, + right_items[0]->self_scm ())); + + /* + Set distance constraints for loose columns + */ + Drul_array<Grob *> next_door; + next_door[LEFT] = cols->elem (i - 1); + next_door[RIGHT] = cols->elem (i + 1); + Direction d = LEFT; + Drul_array<Real> dists (0, 0); + + do + { + dists[d] = 0.0; + Item *lc = dynamic_cast<Item *> ((d == LEFT) ? next_door[LEFT] : c); + Item *rc = dynamic_cast<Item *> (d == LEFT ? c : next_door[RIGHT]); + + + extract_grob_set (lc, "spacing-wishes", wishes); + for (int k = wishes.size(); k--;) + { + Grob *sp = wishes[k]; + if (Note_spacing::left_column (sp) != lc + || Note_spacing::right_column (sp) != rc) + continue; + + Real space, fixed; + fixed = 0.0; + bool dummy; + + if (d == LEFT) + { + /* + The note spacing should be taken from the musical + columns. + + */ + Real base = note_spacing (me, lc, rc, options, &dummy); + Note_spacing::get_spacing (sp, rc, base, increment, &space, &fixed); + + space -= increment; + + dists[d] = max (dists[d], space); + } + else + { + Real space, fixed_space; + Staff_spacing::get_spacing_params (sp, + &space, &fixed_space); + + dists[d] = max (dists[d], fixed_space); + } + } + } + while (flip (&d) != LEFT); + + Rod r; + r.distance_ = dists[LEFT] + dists[RIGHT]; + r.item_drul_[LEFT] = dynamic_cast<Item *> (cols->elem (i - 1)); + r.item_drul_[RIGHT] = dynamic_cast<Item *> (cols->elem (i + 1)); + + r.add_to_cols (); + } + else + { + newcols.push (c); + } + } + + *cols = newcols; +} + +/* + Set neighboring columns determined by the spacing-wishes grob property. +*/ +void +Spacing_spanner::set_explicit_neighbor_columns (Link_array<Grob> const &cols) +{ + for (int i = 0; i < cols.size (); i++) + { + SCM right_neighbors = Grob_array::make_array (); + Grob_array *rn_arr = unsmob_grob_array (right_neighbors); + int min_rank = 100000; // inf. + + extract_grob_set (cols[i], "spacing-wishes", wishes); + for (int k = wishes.size(); k--;) + { + Item *wish = dynamic_cast<Item *> ( wishes[k]); + + Item *lc = wish->get_column (); + Grob *right = Note_spacing::right_column (wish); + + if (!right) + continue; + + Item *rc = dynamic_cast<Item *> (right); + + int right_rank = Paper_column::get_rank (rc); + int left_rank = Paper_column::get_rank (lc); + + /* + update the left column. + */ + if (right_rank <= min_rank) + { + if (right_rank < min_rank) + rn_arr->clear (); + + min_rank = right_rank; + rn_arr->add (wish); + } + + /* + update the right column of the wish. + */ + int maxrank = 0; + + extract_grob_set (rc, "left-neighbors", lns_arr); + if (lns_arr.size ()) + { + Item *it = dynamic_cast<Item *> (lns_arr.top()); + maxrank = Paper_column::get_rank (it->get_column ()); + } + + if (left_rank >= maxrank) + { + + if (left_rank > maxrank) + { + Grob_array *ga = unsmob_grob_array (rc->get_object ("left-neighbors")); + if (ga) + ga->clear (); + } + + Pointer_group_interface::add_grob (rc, ly_symbol2scm ("left-neighbors"), wish); + } + } + + if (rn_arr->size ()) + { + cols[i]->set_object ("right-neighbors", right_neighbors); + } + } +} + +/* + Set neighboring columns that have no left/right-neighbor set + yet. Only do breakable non-musical columns, and musical columns. +*/ +void +Spacing_spanner::set_implicit_neighbor_columns (Link_array<Grob> const &cols) +{ + for (int i = 0; i < cols.size (); i++) + { + Item *it = dynamic_cast<Item *> (cols[i]); + if (!Item::is_breakable (it) && !Paper_column::is_musical (it)) + continue; + + // it->breakable || it->musical + + /* + sloppy with typing left/right-neighbors should take list, but paper-column found instead. + */ + extract_grob_set (cols[i], "left-neighbors", lns); + if (lns.is_empty () && i ) + { + SCM ga_scm = Grob_array::make_array(); + Grob_array *ga = unsmob_grob_array (ga_scm); + ga->add (cols[i-1]); + cols[i]->set_object ("left-neighbors", ga_scm); + } + extract_grob_set (cols[i], "right-neighbors", rns); + if (rns.is_empty () && i < cols.size () - 1) + { + SCM ga_scm = Grob_array::make_array(); + Grob_array *ga = unsmob_grob_array (ga_scm); + ga->add (cols[i+1]); + cols[i]->set_object ("right-neighbors", ga_scm); + } + } +} diff --git a/lily/spacing-spanner.cc b/lily/spacing-spanner.cc index c6d88f508a..80a4511a79 100644 --- a/lily/spacing-spanner.cc +++ b/lily/spacing-spanner.cc @@ -9,362 +9,50 @@ #include <math.h> #include <cstdio> -#include "main.hh" -#include "system.hh" -#include "warn.hh" +#include "spacing-spanner.hh" +#include "paper-column.hh" #include "output-def.hh" #include "paper-score.hh" -#include "paper-column.hh" +#include "system.hh" #include "moment.hh" #include "note-spacing.hh" -#include "misc.hh" +#include "main.hh" #include "warn.hh" -#include "staff-spacing.hh" -#include "spring.hh" -#include "paper-column.hh" +#include "pointer-group-interface.hh" #include "spaceable-grob.hh" -#include "break-align-interface.hh" +#include "staff-spacing.hh" #include "spacing-interface.hh" -#include "pointer-group-interface.hh" -#include "grob-array.hh" - -/* - TODO: this file/class is too complex. Should figure out how to chop - this up even more. -*/ - -class Spacing_spanner -{ -public: - static void standard_breakable_column_spacing (Grob *me, Item *l, Item *r, - Real *fixed, Real *space, Moment); - - static Real default_bar_spacing (Grob *, Grob *, Grob *, Moment); - static Real note_spacing (Grob *, Grob *, Grob *, Moment, bool *); - static Real get_duration_space (Grob *, Moment dur, Rational shortest, bool *); - static Rational find_shortest (Grob *, Link_array<Grob> const &); - static void breakable_column_spacing (Grob *, Item *l, Item *r, Moment); - static void prune_loose_columns (Grob *, Link_array<Grob> *cols, Rational); - static void set_explicit_neighbor_columns (Link_array<Grob> const &cols); - static void set_implicit_neighbor_columns (Link_array<Grob> const &cols); - static void do_measure (Rational, Grob *me, Link_array<Grob> *cols); - static void musical_column_spacing (Grob *, Item *, Item *, Real, Rational); - DECLARE_SCHEME_CALLBACK (set_springs, (SCM)); - static bool has_interface (Grob *); -}; - -/* - Return whether COL is fixed to its neighbors by some kind of spacing - constraint. - - - If in doubt, then we're not loose; the spacing engine should space - for it, risking suboptimal spacing. - - (Otherwise, we might risk core dumps, and other weird stuff.) -*/ -static bool -loose_column (Grob *l, Grob *c, Grob *r) -{ - extract_grob_set (c, "right-neighbors", rns); - extract_grob_set (c, "left-neighbors", lns); - - /* - If this column doesn't have a proper neighbor, we should really - make it loose, but spacing it correctly is more than we can - currently can handle. - - (this happens in the following situation: - - | - | clef G - * - - | | || - | | || - O O || - - - the column containing the clef is really loose, and should be - attached right to the first column, but that is a lot of work for - such a borderline case.) - - */ - if (lns.is_empty () || rns.is_empty ()) - return false; - - Item *l_neighbor = dynamic_cast<Item *> (lns[0]); - Item *r_neighbor = dynamic_cast<Item *> (rns[0]); - - if (!l_neighbor || !r_neighbor) - return false; - - l_neighbor = l_neighbor->get_column (); - r_neighbor = dynamic_cast<Item *> (Note_spacing::right_column (r_neighbor)); - if (l == l_neighbor && r == r_neighbor) - return false; - if (!l_neighbor || !r_neighbor) - return false; - /* - Only declare loose if the bounds make a little sense. This means - some cases (two isolated, consecutive clef changes) won't be - nicely folded, but hey, then don't do that. - */ - if (! ((Paper_column::is_musical (l_neighbor) || Item::is_breakable (l_neighbor)) - && (Paper_column::is_musical (r_neighbor) || Item::is_breakable (r_neighbor)))) - { - return false; - } - - /* - A rather hairy check, but we really only want to move around - clefs. (anything else?) - - in any case, we don't want to move bar lines. - */ - extract_grob_set (c, "elements", elts); - for (int i = elts.size (); i--; ) - { - Grob *g = elts[i]; - if (g && Break_align_interface::has_interface (g)) - { - extract_grob_set (g, "elements", gelts); - for (int j = gelts.size (); j--; ) - { - Grob *h = gelts[j]; - - /* - ugh. -- fix staff-bar name? - */ - if (h && h->get_property ("break-align-symbol") == ly_symbol2scm ("staff-bar")) - return false; - } - } - } - - return true; -} - -/* - Remove columns that are not tightly fitting from COLS. In the - removed columns, set 'between-cols to the columns where it is in - between. -*/ void -Spacing_spanner::prune_loose_columns (Grob *me, Link_array<Grob> *cols, Rational shortest) +Spacing_options::init (Grob*me) { - Link_array<Grob> newcols; - Real increment = robust_scm2double (me->get_property ("spacing-increment"), 1.2); - for (int i = 0; i < cols->size (); i++) - { - if (Item::is_breakable (cols->elem (i)) - || Paper_column::is_musical (cols->elem (i))) - { - newcols.push (cols->elem (i)); - continue; - } - - Grob *c = cols->elem (i); - if (loose_column (cols->elem (i - 1), c, cols->elem (i + 1))) - { - extract_grob_set (c, "right-neighbors", rns_arr); - extract_grob_set (c, "left-neighbors", lns_arr); - - SCM lns = lns_arr.size () ? lns_arr.top()->self_scm () : SCM_BOOL_F; - SCM rns = rns_arr.size () ? rns_arr.top()->self_scm () : SCM_BOOL_F; - - /* - Either object can be non existent, if the score ends - prematurely. - */ - - extract_grob_set (unsmob_grob (rns), "right-items", right_items); - c->set_object ("between-cols", scm_cons (lns, - right_items[0]->self_scm ())); - - /* - Set distance constraints for loose columns - */ - Drul_array<Grob *> next_door; - next_door[LEFT] = cols->elem (i - 1); - next_door[RIGHT] = cols->elem (i + 1); - Direction d = LEFT; - Drul_array<Real> dists (0, 0); - - do - { - dists[d] = 0.0; - Item *lc = dynamic_cast<Item *> ((d == LEFT) ? next_door[LEFT] : c); - Item *rc = dynamic_cast<Item *> (d == LEFT ? c : next_door[RIGHT]); - - - extract_grob_set (lc, "spacing-wishes", wishes); - for (int k = wishes.size(); k--;) - { - Grob *sp = wishes[k]; - if (Note_spacing::left_column (sp) != lc - || Note_spacing::right_column (sp) != rc) - continue; - - Real space, fixed; - fixed = 0.0; - bool dummy; - - if (d == LEFT) - { - /* - The note spacing should be taken from the musical - columns. - - */ - Real base = note_spacing (me, lc, rc, shortest, &dummy); - Note_spacing::get_spacing (sp, rc, base, increment, &space, &fixed); - - space -= increment; - - dists[d] = max (dists[d], space); - } - else - { - Real space, fixed_space; - Staff_spacing::get_spacing_params (sp, - &space, &fixed_space); - - dists[d] = max (dists[d], fixed_space); - } - } - } - while (flip (&d) != LEFT); - - Rod r; - r.distance_ = dists[LEFT] + dists[RIGHT]; - r.item_drul_[LEFT] = dynamic_cast<Item *> (cols->elem (i - 1)); - r.item_drul_[RIGHT] = dynamic_cast<Item *> (cols->elem (i + 1)); - - r.add_to_cols (); - } - else - { - newcols.push (c); - } - } - - *cols = newcols; + packed_ = to_boolean (me->get_layout ()->c_variable ("packed")); + uniform_ = to_boolean (me->get_property ("uniform-stretching")); } -/* - Set neighboring columns determined by the spacing-wishes grob property. -*/ -void -Spacing_spanner::set_explicit_neighbor_columns (Link_array<Grob> const &cols) + +Rational +Spacing_spanner::effective_shortest_duration (Grob *me, Link_array<Grob> const &all) { - for (int i = 0; i < cols.size (); i++) + SCM preset_shortest = me->get_property ("common-shortest-duration"); + Rational global_shortest; + if (unsmob_moment (preset_shortest)) { - SCM right_neighbors = Grob_array::make_array (); - Grob_array *rn_arr = unsmob_grob_array (right_neighbors); - int min_rank = 100000; // inf. - - extract_grob_set (cols[i], "spacing-wishes", wishes); - for (int k = wishes.size(); k--;) - { - Item *wish = dynamic_cast<Item *> ( wishes[k]); - - Item *lc = wish->get_column (); - Grob *right = Note_spacing::right_column (wish); - - if (!right) - continue; - - Item *rc = dynamic_cast<Item *> (right); - - int right_rank = Paper_column::get_rank (rc); - int left_rank = Paper_column::get_rank (lc); - - /* - update the left column. - */ - if (right_rank <= min_rank) - { - if (right_rank < min_rank) - rn_arr->clear (); - - min_rank = right_rank; - rn_arr->add (wish); - } - - /* - update the right column of the wish. - */ - int maxrank = 0; - - extract_grob_set (rc, "left-neighbors", lns_arr); - if (lns_arr.size ()) - { - Item *it = dynamic_cast<Item *> (lns_arr.top()); - maxrank = Paper_column::get_rank (it->get_column ()); - } - - if (left_rank >= maxrank) - { - - if (left_rank > maxrank) - { - Grob_array *ga = unsmob_grob_array (rc->get_object ("left-neighbors")); - if (ga) - ga->clear (); - } - - Pointer_group_interface::add_grob (rc, ly_symbol2scm ("left-neighbors"), wish); - } - } - - if (rn_arr->size ()) - { - cols[i]->set_object ("right-neighbors", right_neighbors); - } + global_shortest = unsmob_moment (preset_shortest)->main_part_; } -} - -/* - Set neighboring columns that have no left/right-neighbor set - yet. Only do breakable non-musical columns, and musical columns. -*/ -void -Spacing_spanner::set_implicit_neighbor_columns (Link_array<Grob> const &cols) -{ - for (int i = 0; i < cols.size (); i++) + else { - Item *it = dynamic_cast<Item *> (cols[i]); - if (!Item::is_breakable (it) && !Paper_column::is_musical (it)) - continue; - - // it->breakable || it->musical - - /* - sloppy with typing left/right-neighbors should take list, but paper-column found instead. - */ - extract_grob_set (cols[i], "left-neighbors", lns); - if (lns.is_empty () && i ) - { - SCM ga_scm = Grob_array::make_array(); - Grob_array *ga = unsmob_grob_array (ga_scm); - ga->add (cols[i-1]); - cols[i]->set_object ("left-neighbors", ga_scm); - } - extract_grob_set (cols[i], "right-neighbors", rns); - if (rns.is_empty () && i < cols.size () - 1) - { - SCM ga_scm = Grob_array::make_array(); - Grob_array *ga = unsmob_grob_array (ga_scm); - ga->add (cols[i+1]); - cols[i]->set_object ("right-neighbors", ga_scm); - } + 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; } + MAKE_SCHEME_CALLBACK (Spacing_spanner, set_springs, 1); SCM Spacing_spanner::set_springs (SCM smob) @@ -378,19 +66,11 @@ Spacing_spanner::set_springs (SCM smob) set_explicit_neighbor_columns (all); - 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 = find_shortest (me, all); - if (be_verbose_global) - message (_f ("Global shortest duration is %s", global_shortest.to_string ()) + "\n"); - } - prune_loose_columns (me, &all, global_shortest); + Spacing_options options; + options.init (me); + options.global_shortest_ = effective_shortest_duration (me, all); + + prune_loose_columns (me, &all, &options); set_implicit_neighbor_columns (all); int j = 0; @@ -400,7 +80,7 @@ Spacing_spanner::set_springs (SCM smob) if (Item::is_breakable (sc)) { Link_array<Grob> measure (all.slice (j, i + 1)); - do_measure (global_shortest, me, &measure); + do_measure (me, &measure, &options); j = i; } } @@ -506,8 +186,10 @@ Spacing_spanner::find_shortest (Grob *me, Link_array<Grob> const &cols) (different time sigs) than others, and should be spaced differently. */ void -Spacing_spanner::do_measure (Rational global_shortest, Grob *me, - Link_array<Grob> *cols) +Spacing_spanner::do_measure (Grob *me, + Link_array<Grob> *cols, + Spacing_options const *options + ) { Real headwid = robust_scm2double (me->get_property ("spacing-increment"), 1); for (int i = 0; i < cols->size () - 1; i++) @@ -520,9 +202,9 @@ Spacing_spanner::do_measure (Rational global_shortest, Grob *me, if (Paper_column::is_musical (l)) { - musical_column_spacing (me, lc, rc, headwid, global_shortest); + musical_column_spacing (me, lc, rc, headwid, options); if (Item *rb = r->find_prebroken_piece (LEFT)) - musical_column_spacing (me, lc, rb, headwid, global_shortest); + musical_column_spacing (me, lc, rb, headwid, options); } else { @@ -538,16 +220,16 @@ Spacing_spanner::do_measure (Rational global_shortest, Grob *me, l = 0; if (l && r) - breakable_column_spacing (me, l, r, global_shortest); + breakable_column_spacing (me, l, r, options); if (lb && r) - breakable_column_spacing (me, lb, r, global_shortest); + breakable_column_spacing (me, lb, r, options); if (l && rb) - breakable_column_spacing (me, l, rb, global_shortest); + breakable_column_spacing (me, l, rb, options); if (lb && rb) - breakable_column_spacing (me, lb, rb, global_shortest); + breakable_column_spacing (me, lb, rb, options); } } } @@ -557,84 +239,94 @@ Spacing_spanner::do_measure (Rational global_shortest, Grob *me, spacing parameters INCR and SHORTEST. */ void -Spacing_spanner::musical_column_spacing (Grob *me, Item *lc, Item *rc, Real increment, Rational global_shortest) +Spacing_spanner::musical_column_spacing (Grob *me, Item *lc, Item *rc, + Real increment, + Spacing_options const *options) { bool expand_only = false; - Real base_note_space = note_spacing (me, lc, rc, global_shortest, &expand_only); - + Real base_note_space = note_spacing (me, lc, rc, options, &expand_only); + Real compound_note_space = 0.0; Real compound_fixed_note_space = 0.0; - int wish_count = 0; - extract_grob_set (lc, "right-neighbors", neighbors); - - /* - We adjust the space following a note only if the next note - happens after the current note (this is set in the grob - property SPACING-SEQUENCE. - */ - for (int i = 0; i < neighbors.size (); i++) + if (options->uniform_) { - Grob *wish = neighbors[i]; + compound_note_space = base_note_space; + } + else + { + int wish_count = 0; - Item *wish_rcol = Note_spacing::right_column (wish); - if (Note_spacing::left_column (wish) != lc - || (wish_rcol != rc && wish_rcol != rc->original_)) - continue; + extract_grob_set (lc, "right-neighbors", neighbors); /* - This is probably a waste of time in the case of polyphonic - music. */ - if (Note_spacing::has_interface (wish)) + We adjust the space following a note only if the next note + happens after the current note (this is set in the grob + property SPACING-SEQUENCE. + */ + for (int i = 0; i < neighbors.size (); i++) { - Real space = 0.0; - Real fixed = 0.0; + Grob *wish = neighbors[i]; - Note_spacing::get_spacing (wish, rc, base_note_space, increment, &space, &fixed); + Item *wish_rcol = Note_spacing::right_column (wish); + if (Note_spacing::left_column (wish) != lc + || (wish_rcol != rc && wish_rcol != rc->original_)) + continue; - compound_note_space = compound_note_space + space; - compound_fixed_note_space = compound_fixed_note_space + fixed; - wish_count++; - } - } + /* + This is probably a waste of time in the case of polyphonic + music. */ + if (Note_spacing::has_interface (wish)) + { + Real space = 0.0; + Real fixed = 0.0; - if (Paper_column::when_mom (rc).grace_part_ - && !Paper_column::when_mom (lc).grace_part_) - { - /* - Ugh. 0.8 is arbitrary. - */ - compound_note_space *= 0.8; - } + Note_spacing::get_spacing (wish, rc, base_note_space, increment, &space, &fixed); - if (compound_note_space < 0 || wish_count == 0) - { - compound_note_space = base_note_space; - compound_fixed_note_space = increment; - } - else - { - compound_note_space /= wish_count; - compound_fixed_note_space /= wish_count; - } + compound_note_space = compound_note_space + space; + compound_fixed_note_space = compound_fixed_note_space + fixed; + wish_count++; + } + } - /* - Whatever we do, the fixed space is smaller than the real - space. + if (Paper_column::when_mom (rc).grace_part_ + && !Paper_column::when_mom (lc).grace_part_) + { + /* + Ugh. 0.8 is arbitrary. + */ + compound_note_space *= 0.8; + } - TODO: this criterion is discontinuous in the derivative. - Maybe it should be continuous? - */ - compound_fixed_note_space = min (compound_fixed_note_space, compound_note_space); + if (compound_note_space < 0 || wish_count == 0) + { + compound_note_space = base_note_space; + compound_fixed_note_space = increment; + } + else + { + compound_note_space /= wish_count; + compound_fixed_note_space /= wish_count; + } + + /* + Whatever we do, the fixed space is smaller than the real + space. - bool packed = to_boolean (me->get_layout ()->c_variable ("packed")); + TODO: this criterion is discontinuous in the derivative. + Maybe it should be continuous? + */ + compound_fixed_note_space = min (compound_fixed_note_space, + compound_note_space); + } + Real inverse_strength = 1.0; Real distance = 1.0; /* TODO: make sure that the space doesn't exceed the right margin. */ - if (packed) + if (options->packed_) { /* In packed mode, pack notes as tight as possible. This makes @@ -658,69 +350,11 @@ Spacing_spanner::musical_column_spacing (Grob *me, Item *lc, Item *rc, Real incr } /* - The one-size-fits all spacing. It doesn't take into account - different spacing wishes from one to the next column. -*/ -void -Spacing_spanner::standard_breakable_column_spacing (Grob *me, Item *l, Item *r, - Real *fixed, Real *space, - Moment shortest) -{ - *fixed = 0.0; - Direction d = LEFT; - Drul_array<Item *> cols (l, r); - - do - { - if (!Paper_column::is_musical (cols[d])) - { - /* - Tied accidentals over barlines cause problems, so lets see - what happens if we do this for non musical columns only. - */ - Interval lext = cols[d]->extent (cols [d], X_AXIS); - if (!lext.is_empty ()) - *fixed += -d * lext[-d]; - } - } - while (flip (&d) != LEFT); - - if (l->is_breakable (l) && r->is_breakable (r)) - { - Moment *dt = unsmob_moment (l->get_property ("measure-length")); - Moment mlen (1); - if (dt) - mlen = *dt; - - Real incr = robust_scm2double (me->get_property ("spacing-increment"), 1); - - *space = *fixed + incr * double (mlen.main_part_ / shortest.main_part_) * 0.8; - } - else - { - Moment dt = Paper_column::when_mom (r) - Paper_column::when_mom (l); - - if (dt == Moment (0, 0)) - { - /* - In this case, Staff_spacing should handle the job, - using dt when it is 0 is silly. - */ - *space = *fixed + 0.5; - } - else - { - bool dummy; - *space = *fixed + get_duration_space (me, dt, shortest.main_part_, &dummy); - } - } -} - -/* Read hints from L and generate springs. */ void -Spacing_spanner::breakable_column_spacing (Grob *me, Item *l, Item *r, Moment shortest) +Spacing_spanner::breakable_column_spacing (Grob *me, Item *l, Item *r, + Spacing_options const *options) { Real compound_fixed = 0.0; Real compound_space = 0.0; @@ -771,7 +405,7 @@ Spacing_spanner::breakable_column_spacing (Grob *me, Item *l, Item *r, Moment sh if (compound_space <= 0.0 || !wish_count) { standard_breakable_column_spacing (me, l, r, &compound_fixed, &compound_space, - shortest); + options); wish_count = 1; } else @@ -795,125 +429,6 @@ Spacing_spanner::breakable_column_spacing (Grob *me, Item *l, Item *r, Moment sh Spaceable_grob::add_spring (l, r, distance, inverse_strength); } -/** - Get the measure wide ant for arithmetic spacing. -*/ -Real -Spacing_spanner::get_duration_space (Grob *me, Moment d, Rational shortest, bool *expand_only) -{ - Real k = robust_scm2double (me->get_property ("shortest-duration-space"), 1); - Real incr = robust_scm2double (me->get_property ("spacing-increment"), 1); - - if (d < shortest) - { - /* - We don't space really short notes using the log of the - duration, since it would disproportionally stretches the long - notes in a piece. In stead, we use geometric spacing with constant 0.5 - (i.e. linear.) - - This should probably be tunable, to use other base numbers. - - In Mozart hrn3 by EB., we have 8th note = 3.9 mm (total), 16th note = - 3.6 mm (total). head-width = 2.4, so we 1.2mm for 16th, 1.5 - mm for 8th. (white space), suggesting that we use - - (1.2 / 1.5)^{-log2(duration ratio)} - - - */ - Rational ratio = d.main_part_ / shortest; - - return ((k - 1) + double (ratio)) * incr; - } - else - { - /* - John S. Gourlay. ``Spacing a Line of Music, '' Technical - Report OSU-CISRC-10/87-TR35, Department of Computer and - Information Science, The Ohio State University, 1987. - */ - Real log = log_2 (shortest); - k -= log; - Rational compdur = d.main_part_ + d.grace_part_ / Rational (3); - *expand_only = false; - - return (log_2 (compdur) + k) * incr; - } -} - -Real -Spacing_spanner::note_spacing (Grob *me, Grob *lc, Grob *rc, - Moment shortest, bool *expand_only) -{ - Moment shortest_playing_len = 0; - SCM s = lc->get_property ("shortest-playing-duration"); - - if (unsmob_moment (s)) - shortest_playing_len = *unsmob_moment (s); - - if (! shortest_playing_len.to_bool ()) - { - programming_error ("can't find a ruling note at " + Paper_column::when_mom (lc).to_string ()); - shortest_playing_len = 1; - } - - Moment lwhen = Paper_column::when_mom (lc); - Moment rwhen = Paper_column::when_mom (rc); - - Moment delta_t = rwhen - lwhen; - if (!Paper_column::is_musical (rc)) - { - /* - when toying with mmrests, it is possible to have musical - column on the left and non-musical on the right, spanning - several measures. - */ - - Moment *dt = unsmob_moment (rc->get_property ("measure-length")); - if (dt) - { - delta_t = min (delta_t, *dt); - - /* - The following is an extra safety measure, such that - the length of a mmrest event doesn't cause havoc. - */ - shortest_playing_len = min (shortest_playing_len, *dt); - } - } - Real dist = 0.0; - - /* - In normal situations, the next column is at most - SHORTEST_PLAYING_LEN away. However chord-tremolos do funky faking stuff - with durations, invalidating this assumption. Here we kludge - around to get chord tremolos to behave properly. - - */ - shortest_playing_len = max (shortest_playing_len, delta_t); - if (delta_t.main_part_ && !lwhen.grace_part_) - { - dist = get_duration_space (me, shortest_playing_len, - shortest.main_part_, expand_only); - dist *= double (delta_t.main_part_ / shortest_playing_len.main_part_); - } - else if (delta_t.grace_part_) - { - /* - TODO: figure out how to space grace notes. - */ - dist = get_duration_space (me, shortest, shortest.main_part_, expand_only); - - Real grace_fact - = robust_scm2double (me->get_property ("grace-space-factor"), 1); - - dist *= grace_fact; - } - - return dist; -} - ADD_INTERFACE (Spacing_spanner, "spacing-spanner-interface", "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" @@ -927,7 +442,7 @@ ADD_INTERFACE (Spacing_spanner, "spacing-spanner-interface", "quarter note is followed by 3 NHW, the half by 4 NHW, etc.\n", "grace-space-factor spacing-increment base-shortest-duration " - "shortest-duration-space common-shortest-duration" + "shortest-duration-space common-shortest-duration uniform-stretching" ); diff --git a/mf/feta-arrow.mf b/mf/feta-arrow.mf index 6a32af187e..361ee22dfb 100644 --- a/mf/feta-arrow.mf +++ b/mf/feta-arrow.mf @@ -10,6 +10,9 @@ fet_begingroup ("arrowheads"); +% +% To consider: we could put arrow heads at their real Unicode locations. +% % % Setup paths for upper half of arrow head pointing right. diff --git a/scm/define-grob-properties.scm b/scm/define-grob-properties.scm index 3e92408fad..bb10de870a 100644 --- a/scm/define-grob-properties.scm +++ b/scm/define-grob-properties.scm @@ -114,6 +114,14 @@ appearance of a bar line at the line break. It takes a glyph and break-direction and returns the glyph at a line break.") (break-overshoot ,number-pair? "How much does a broken spanner stick out of its bounds?") + (bracket-visibility ,boolean-or-symbol? "This controls the +visibility of the tuplet bracket. Setting it to false will prevent +printing of the bracket. Setting the property to @code{'if-no-beam} +will make it print only if there is no beam associated with this +tuplet bracket.") + (break-visibility ,vector? "A vector of 3 booleans, #(end-of-line unbroken begin-of-line). +#t means visible, #f means killed.") + (breakable ,boolean? "Can this object appear at a line break, like clefs and bar lines?") (c0-position ,integer? "An integer indicating the position of @@ -175,6 +183,10 @@ the vertical edges '(@var{left-height} . @var{right-height}).") edges '(@var{left-text} . @var{right-text}).") (eccentricity ,number? "How asymmetrical to make a slur. Positive means move the center to the right.") + (enclose-bounds ,number? + "How much of the bound a spanner should enclose: ++1 = completely, 0 = center, -1 not at all.") + (expand-limit ,integer? "maximum number of measures expanded in church rests.") ;; remove me? @@ -191,11 +203,13 @@ engine is completely oblivious to it.") (finger-code ,symbol? "Code for the type of fingering indication in a fret diagram. Options include @code{none}, @code{in-dot}, and @code{below-string}.") + (flag-count ,number? "The number of tremolo beams.") (flag-style ,symbol? "a string determining what style of flag-glyph is typeset on a Stem. Valid options include @code{()} and @code{mensural}. Additionally, @code{\"no-flag\"} switches off the flag.") + (flag-width-function ,procedure? "Procedure that computes the width of a half-beam (a non-connecting beam.).") (font-family ,symbol? "The font family is the broadest category for selecting text fonts. Options include: @code{sans}, @code{roman} ") (font-encoding ,symbol? "The font encoding is the broadest @@ -314,8 +328,6 @@ this long. This requires an appropriate routine for the @code{spacing-procedure} property.") (minimum-space ,ly:dimension? "Minimum distance that the victim should move (after padding).") - (print-function ,procedure? "Function taking grob as argument, -returning a @code{Stencil} object.") (neutral-direction ,ly:dir? "Which direction to take in the center of the staff.") (neutral-position ,number? "Position (in half staff spaces) where @@ -337,10 +349,6 @@ include @code{roman-lower}, @code{roman-upper}, and @code{arabic}.") (old-accidentals ,list? "List of @code{(@var{pitch} . @var{accidental}) pairs.}") - (enclose-bounds ,number? - "How much of the bound a spanner should enclose: -+1 = completely, 0 = center, -1 not at all.") - (padding ,ly:dimension? "Add this much extra space between objects that are next to each other.") (page-penalty ,number? "Penalty for page break at @@ -356,7 +364,9 @@ as a real penalty.") "Pair of staff coordinates @code{(@var{left} . @var{right})}, where both @var{left} and @var{right} are in the staff-space unit of the current staff.") - + (print-function ,procedure? "Function taking grob as argument, +returning a @code{Stencil} object.") + (ratio ,number? "Parameter for slur shape. The higher this number, the quicker the slur attains it @code{height-limit}.") (remove-first ,boolean? "Remove the first staff of a orchestral score?") @@ -414,6 +424,7 @@ parameters. The routine is called after expressed in global staffspace.") (staff-position ,number? "Vertical position, measured in half staff spaces, counted from the middle line.") + (staffline-clearance ,ly:dimension? "How far away ties keep from staff lines.") (stemlet-length ,number? "How long should a stem over a rest be?") @@ -457,17 +468,12 @@ reading this property.") (transparent ,boolean? "This is almost the same as setting @code{print-function} to @code{#f}, but this retains the dimensions of this grob, which means that grobs can be erased individually.") - (bracket-visibility ,boolean-or-symbol? "This controls the -visibility of the tuplet bracket. Setting it to false will prevent -printing of the bracket. Setting the property to @code{'if-no-beam} -will make it print only if there is no beam associated with this -tuplet bracket.") + (uniform-stretching ,boolean? "If set, items stretch proportional +to their durations. This looks better in complex polyphonic patterns") + (number-visibility ,boolean-or-symbol? "Like @code{bracket-visibility}, but for the number.") - (break-visibility ,vector? "A vector of 3 booleans, #(end-of-line unbroken begin-of-line). -#t means visible, #f means killed.") - (flag-count ,number? "The number of tremolo beams.") (when ,ly:moment? "Global time step associated with this column happen?") diff --git a/scm/framework-gnome.scm b/scm/framework-gnome.scm index 17f7e43525..826a159620 100644 --- a/scm/framework-gnome.scm +++ b/scm/framework-gnome.scm @@ -201,6 +201,10 @@ (ly:input-location music-origin) #f))) +;; todo: how to integrate nicely? +;(define-public (tweak-grob-property grob sym val) +; (set! (ly:grob-property grob sym) val)) + (define-method (tweak (go <gnome-outputter>) item offset) (let* ((grob (hashq-ref (item-grobs go) item #f)) diff --git a/scm/lily.scm b/scm/lily.scm index b68ddc4506..c91fcb2b88 100644 --- a/scm/lily.scm +++ b/scm/lily.scm @@ -346,13 +346,15 @@ The syntax is the same as `define*-public'." (lambda (x y) (string<? (car x) (car y))))))))) -(define-public (tweak-grob-property grob sym val) - (set! (ly:grob-property grob sym) val)) - ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (define-public (lilypond-main files) "Entry point for LilyPond." + (define (no-files-handler) + (ly:usage) + (exit 2)) + + (if (null? files) (no-files-handler)) @@ -366,10 +368,6 @@ The syntax is the same as `define*-public'." (ly:message "") (exit 0))))) -(define (no-files-handler) - (ly:usage) - (exit 2)) - (define-public (lilypond-all files) (let* ((failed '()) (handler (lambda (key failed-file) |