X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=lily%2Fspacing-spanner.cc;h=89cf829cfcaf8717ce4f87196b45870f9219fec4;hb=d145acbbd1250d861b3e8559d40c342273b2bb63;hp=f3c69d9976c47da7287bc2c2c4ef1f8e61892b88;hpb=7cacca4ac9e71dc19ba1c1fc600d2bd4ac4b9b26;p=lilypond.git diff --git a/lily/spacing-spanner.cc b/lily/spacing-spanner.cc index f3c69d9976..89cf829cfc 100644 --- a/lily/spacing-spanner.cc +++ b/lily/spacing-spanner.cc @@ -3,14 +3,17 @@ source file of the GNU LilyPond music typesetter - (c) 1999--2002 Han-Wen Nienhuys + (c) 1999--2003 Han-Wen Nienhuys */ #include #include -#include "line-of-score.hh" +#include "main.hh" +#include "system.hh" +#include "warn.hh" +#include "paper-def.hh" #include "paper-score.hh" #include "paper-column.hh" #include "item.hh" @@ -22,23 +25,21 @@ #include "spring.hh" #include "paper-column.hh" #include "spaceable-grob.hh" +#include "break-align-interface.hh" +#include "spacing-interface.hh" -/* - paper-column: - - Don't be confused by right-items: each spacing wish can also contain - a number of items, with which a spacing constraint may be kept. It's - a little baroque, but it might come in handy later on? - - */ 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 (Link_array const &); - static void breakable_column_spacing (Item* l, Item *r); + static Rational find_shortest (Grob *, Link_array const &); + static void breakable_column_spacing (Grob*, Item* l, Item *r, Moment); static void find_loose_columns () {} static void prune_loose_colunms (Grob*,Link_array *cols, Rational); static void find_loose_columns (Link_array cols); @@ -47,11 +48,19 @@ public: static void do_measure (Rational, Grob*me,Link_array *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) @@ -89,7 +98,7 @@ loose_column (Grob *l, Grob *c, Grob *r) if (!l_neighbor || !r_neighbor) return false; - l_neighbor = l_neighbor->column_l(); + l_neighbor = l_neighbor->get_column (); r_neighbor = dynamic_cast (Note_spacing::right_column (r_neighbor)); if (l == l_neighbor && r == r_neighbor) @@ -98,26 +107,45 @@ loose_column (Grob *l, Grob *c, Grob *r) 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::musical_b (l_neighbor) || Item::breakable_b (l_neighbor)) - && (Paper_column::musical_b (r_neighbor) || Item::breakable_b (r_neighbor))) + if(! ((Paper_column::musical_b (l_neighbor) || Item::breakable_b (l_neighbor)) + && (Paper_column::musical_b (r_neighbor) || Item::breakable_b (r_neighbor))) ) { - return true; + return false; } /* - If in doubt: we're not loose; the spacing engine should space for - it, risking suboptimal spacing. + A rather hairy check, but we really only want to move around clefs. (anything else?) - (Otherwise, we might risk core dumps, and other weird stuff.) + in any case, we don't want to move bar lines. + */ + for (SCM e = c->get_grob_property ("elements"); gh_pair_p (e); e = gh_cdr (e)) + { + Grob * g = unsmob_grob (gh_car (e)); + if (g && Break_align_interface::has_interface (g)) + { + for (SCM s = g->get_grob_property ("elements"); gh_pair_p (s); + s = gh_cdr (s)) + { + Grob *h = unsmob_grob (gh_car (s)); - */ - return false; + /* + ugh. -- fix staff-bar name? + */ + if (h && h->get_grob_property ("break-align-symbol") == ly_symbol2scm ("staff-bar")) + return false; + } + } + } + + return true; } /* @@ -180,18 +208,37 @@ Spacing_spanner::prune_loose_colunms (Grob*me,Link_array *cols, Rational s Real space, fixed; fixed = 0.0; - bool expand_only; - Real base = note_spacing (me, lc, rc, shortest, &expand_only); - Note_spacing::get_spacing (sp, rc, base, increment, &space, &fixed); - space -=base; + 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] = dists[d] >? space; + } + else + { + Real space, fixed_space; + Staff_spacing::get_spacing_params (sp, + &space, &fixed_space); + + dists[d] = dists[d] >? fixed_space; + } - dists[d] = dists[d] >? space; } } while (flip (&d) != LEFT); Rod r; - r.distance_f_ = dists[LEFT] + dists[RIGHT]; + r.distance_ = dists[LEFT] + dists[RIGHT]; r.item_l_drul_[LEFT] = dynamic_cast (cols->elem(i-1)); r.item_l_drul_[RIGHT] = dynamic_cast (cols->elem (i+1)); @@ -223,7 +270,7 @@ Spacing_spanner::set_explicit_neighbor_columns (Link_array cols) { Item * wish = dynamic_cast (unsmob_grob (gh_car (s))); - Item * lc = wish->column_l (); + Item * lc = wish->get_column (); Grob * right = Note_spacing::right_column (wish); if (!right) @@ -231,8 +278,8 @@ Spacing_spanner::set_explicit_neighbor_columns (Link_array cols) Item * rc = dynamic_cast (right); - int right_rank = Paper_column::rank_i (rc); - int left_rank = Paper_column::rank_i (lc); + int right_rank = Paper_column::get_rank (rc); + int left_rank = Paper_column::get_rank (lc); /* update the left column. @@ -255,7 +302,7 @@ Spacing_spanner::set_explicit_neighbor_columns (Link_array cols) && unsmob_grob (gh_car (left_neighs))) { Item * it = dynamic_cast (unsmob_grob (gh_car (left_neighs))); - maxrank = Paper_column::rank_i (it->column_l()); + maxrank = Paper_column::get_rank (it->get_column ()); } if (left_rank >= maxrank) @@ -314,11 +361,24 @@ Spacing_spanner::set_springs (SCM smob) { Grob *me = unsmob_grob (smob); - Link_array all (me->pscore_l_->line_l_->column_l_arr ()) ; + Link_array all (me->pscore_->system_->columns ()) ; set_explicit_neighbor_columns (all); - Rational global_shortest = find_shortest (all); + SCM preset_shortest = me->get_grob_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 (verbose_global_b) + { + progress_indication (_f("Global shortest duration is %s\n", global_shortest.to_string ())); + } + } prune_loose_colunms (me, &all, global_shortest); set_implicit_neighbor_columns (all); @@ -350,7 +410,7 @@ Spacing_spanner::set_springs (SCM smob) */ Rational -Spacing_spanner::find_shortest (Link_array const &cols) +Spacing_spanner::find_shortest (Grob *me, Link_array const &cols) { /* ascending in duration @@ -417,13 +477,14 @@ Spacing_spanner::find_shortest (Link_array const &cols) max_count = counts[i]; } - // printf ("Den %d/%d, c %d\n", durations[i].num (), durations[i].den (), counts[i]); + // printf ("duration %d/%d, count %d\n", durations[i].num (), durations[i].den (), counts[i]); } - /* - TODO: 1/8 should be adjustable? - */ + SCM bsd = me->get_grob_property ("base-shortest-duration"); Rational d = Rational (1,8); + if (Moment *m = unsmob_moment (bsd)) + d = m->main_part_; + if (max_idx >= 0) d = d *cols) if (!Paper_column::musical_b (l)) { - breakable_column_spacing (l, r); + breakable_column_spacing (me, l, r, shortest); /* @@ -464,12 +525,12 @@ Spacing_spanner::do_measure (Rational shortest, Grob*me, Link_array *cols) Item *rb = r->find_prebroken_piece (LEFT); if (lb) - breakable_column_spacing (lb,r); + breakable_column_spacing (me, lb,r, shortest); if (rb) - breakable_column_spacing (l, rb); + breakable_column_spacing (me, l, rb, shortest); if (lb && rb) - breakable_column_spacing (lb, rb); + breakable_column_spacing (me, lb, rb, shortest); continue ; } @@ -483,7 +544,8 @@ Spacing_spanner::do_measure (Rational shortest, Grob*me, Link_array *cols) /* - Generate the space between two musical columns LC and RC, given spacing parameters INCR and SHRTEST. + Generate the space between two musical columns LC and RC, given + spacing parameters INCR and SHORTEST. */ void Spacing_spanner::musical_column_spacing (Grob *me, Item * lc, Item *rc, Real increment, Rational shortest) @@ -507,7 +569,7 @@ Spacing_spanner::musical_column_spacing (Grob *me, Item * lc, Item *rc, Real inc Item *wish_rcol = Note_spacing::right_column (wish); if (Note_spacing::left_column (wish) != lc - || (wish_rcol != rc && wish_rcol != rc->original_l_)) + || (wish_rcol != rc && wish_rcol != rc->original_)) continue; /* @@ -522,27 +584,120 @@ Spacing_spanner::musical_column_spacing (Grob *me, Item * lc, Item *rc, Real inc max_note_space = max_note_space >? space; max_fixed_note_space = max_fixed_note_space >? fixed; } - } + if (Paper_column::when_mom (rc).grace_part_ && + !Paper_column::when_mom (lc).grace_part_) + { + /* + Ugh. 0.8 is arbitrary. + */ + max_note_space *= 0.8; + } + if (max_note_space < 0) { max_note_space = base_note_space; - max_fixed_note_space = increment; + max_fixed_note_space = increment; } - Spaceable_grob::add_spring (lc, rc, max_note_space, 1 / (max_note_space -max_fixed_note_space), expand_only); + bool ragged = to_boolean (me->get_paper ()->get_scmvar ("raggedright")); + + /* + Whatever we do, the fixed space is smaller than the real + space. + + TODO: this criterion is discontinuous in the derivative. + Maybe it should be continuous? + */ + max_fixed_note_space = max_fixed_note_space cols(l,r); + + do + { + if (!Paper_column::musical_b (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); + *fixed += -d * lext[-d]; + } + } + while (flip (&d) != LEFT); + + if (l->breakable_b (l) && r->breakable_b(r)) + { + Moment *dt = unsmob_moment (l->get_grob_property ("measure-length")); + Moment mlen (1); + if (dt) + mlen = *dt; + + Real incr = gh_scm2double (me->get_grob_property ("spacing-increment")); + + *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); + 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) { Real max_fixed = -infinity_f; Real max_space = -infinity_f; + + standard_breakable_column_spacing (me, l, r, &max_fixed, &max_space , + shortest); for (SCM s = l->get_grob_property ("spacing-wishes"); gh_pair_p (s); s = gh_cdr (s)) @@ -560,10 +715,20 @@ Spacing_spanner::breakable_column_spacing (Item* l, Item *r) pointer munging. */ - assert (spacing_grob-> column_l () == l); + assert (spacing_grob-> get_column () == l); Staff_spacing::get_spacing_params (spacing_grob, - &space, &fixed_space); + &space, &fixed_space); + + if (Paper_column::when_mom (r).grace_part_) + { + /* + Correct for grace notes. + + Ugh. The 0.8 is arbitrary. + */ + space *= 0.8; + } if (space > max_space) { max_space = space; @@ -571,9 +736,23 @@ Spacing_spanner::breakable_column_spacing (Item* l, Item *r) } } + + + if (isinf (max_space)) { - programming_error ("No pref spacing found"); + /* + One situation where this can happen is when there is a column + that only serves as a spanning point for a short staff-symbol. + + ===============X=== + + |=======Y + + + (here no StaffSpacing from Y to X is found.) + */ + programming_error ("No StaffSpacing wishes found"); max_space = 2.0; max_fixed = 1.0; } @@ -590,8 +769,17 @@ Spacing_spanner::breakable_column_spacing (Item* l, Item *r) Hmm. we do 1/0 in the next thing. Perhaps we should check if this works on all architectures. */ - - Spaceable_grob::add_spring (l, r, max_space, 1/(max_space - max_fixed), false); + + /* + There used to be code that changed spacing depending on + raggedright setting. Ugh. + + Do it more cleanly, or rename the property. + + */ + Real strength = 1 / (max_space - max_fixed); + Real distance = max_space; + Spaceable_grob::add_spring (l, r, distance, strength, false); } @@ -602,28 +790,46 @@ Real Spacing_spanner::get_duration_space (Grob*me, Moment d, Rational shortest, bool * expand_only) { Real k = gh_scm2double (me->get_grob_property ("shortest-duration-space")); - + Real incr = gh_scm2double (me->get_grob_property ("spacing-increment")); + 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; - + +#if 0 *expand_only = true; - return (0.5 + 0.5 * double (ratio)) * k ; +#endif + return ((k-1) + double (ratio)) * incr; } else { /* - @see - 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. + 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) * gh_scm2double (me->get_grob_property ("spacing-increment")); + return (log_2 (compdur) + k) * incr; } } @@ -639,14 +845,25 @@ Spacing_spanner::note_spacing (Grob*me, Grob *lc, Grob *rc, if (! shortest_playing_len.to_bool ()) { - programming_error ("can't find a ruling note at " + Paper_column::when_mom (lc).str ()); + programming_error ("can't find a ruling note at " + Paper_column::when_mom (lc).to_string ()); shortest_playing_len = 1; } - - Moment delta_t = Paper_column::when_mom (rc) - Paper_column::when_mom (lc); + + Moment lwhen = Paper_column::when_mom (lc); + Moment rwhen = Paper_column::when_mom (rc); + + Moment delta_t = rwhen - lwhen; Real dist = 0.0; - if (delta_t.main_part_) + /* + 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 = 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_); @@ -670,3 +887,24 @@ Spacing_spanner::note_spacing (Grob*me, Grob *lc, Grob *rc, 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" +"note gets shortest-duration-space. Notes that are even shorter are\n" +"spaced proportonial to their duration.\n" +"\n" +"Typically, the increment is the width of a black note head. In a\n" +"piece with lots of 8th notes, and some 16th notes, the eighth note\n" +"gets 2 note heads width (i.e. the space following a note is 1 note\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 base-shortest-duration shortest-duration-space common-shortest-duration"); + + + +ADD_INTERFACE (Spacing_interface,"spacing-interface", + "Something to do with line breaking and spacing. Kill this one after determining line breaks.", + ""); +