source file of the GNU LilyPond music typesetter
- (c) 1999--2001 Han-Wen Nienhuys <hanwen@cs.uu.nl>
+ (c) 1999--2002 Han-Wen Nienhuys <hanwen@cs.uu.nl>
*/
-#include "new-spacing-spanner.hh"
+
#include "paper-column.hh"
#include "dimensions.hh"
#include "paper-def.hh"
#include "line-of-score.hh"
#include "misc.hh"
#include "separation-item.hh"
+#include "spanner.hh"
+#include "spring.hh"
+class New_spacing_spanner
+{
+public:
+ static void set_interface (Grob*);
+ static void do_measure (Grob*,Link_array<Grob> *) ;
+ static void stretch_to_regularity (Grob*, Array<Spring> *, Link_array<Grob> const &);
+ static void breakable_column_spacing (Item* l, Item *r);
+ DECLARE_SCHEME_CALLBACK (set_springs, (SCM ));
+ static Real default_bar_spacing (Grob*,Grob*,Grob*,Moment) ;
+ static Real note_spacing (Grob*,Grob*,Grob*,Moment) ;
+ static Real get_duration_space (Grob*,Moment dur, Moment shortest) ;
+ static void prune_loose_colunms (Link_array<Grob>*);
+};
void
New_spacing_spanner::set_interface (Grob*me)
me->set_extent_callback (SCM_EOL, Y_AXIS) ;
}
+/*
+ Remove all columns that are not tightly
+ fitting part of the spacing problem.
+ */
+void
+New_spacing_spanner::prune_loose_colunms (Link_array<Grob> *cols)
+{
+ for (int i = cols->size(); i--;)
+ {
+ SCM between = cols->elem(i)->get_grob_property ("between-cols");
+ if (!gh_pair_p (between))
+ continue;
+
+ Item * l = dynamic_cast<Item*> (unsmob_grob (gh_car (between)));
+ Item * r = dynamic_cast<Item*> (unsmob_grob (gh_cdr (between)));
+ if (l->column_l () != cols->elem (i-1)
+ || r->column_l () != cols->elem (i +1))
+ {
+ cols->del (i);
+ }
+ }
+}
+
/*
The algorithm is partly taken from :
*/
void
-New_spacing_spanner::do_measure (Grob*me, Link_array<Grob> const & cols)
+New_spacing_spanner::do_measure (Grob*me, Link_array<Grob> *cols)
{
- Moment shortest;
- Moment mean_shortest;
+ Moment shortest_in_measure;
/*
space as if this duration is present.
*/
Moment base_shortest_duration = *unsmob_moment (me->get_grob_property ("maximum-duration-for-spacing"));
- shortest.set_infinite (1);
+ shortest_in_measure.set_infinite (1);
+
+ prune_loose_colunms (cols);
- int n = 0;
- for (int i =0 ; i < cols.size (); i++)
+ for (int i =0 ; i < cols->size (); i++)
{
- if (Paper_column::musical_b (cols[i]))
+ if (Paper_column::musical_b (cols->elem (i)))
{
- Moment *when = unsmob_moment (cols[i]->get_grob_property ("when"));
+ Moment *when = unsmob_moment (cols->elem (i)->get_grob_property ("when"));
/*
ignore grace notes for shortest notes.
if (when && when->grace_part_)
continue;
- SCM st = cols[i]->get_grob_property ("shortest-starter-duration");
+ SCM st = cols->elem (i)->get_grob_property ("shortest-starter-duration");
Moment this_shortest = *unsmob_moment (st);
- shortest = shortest <? this_shortest;
- if (!mean_shortest.main_part_.infty_b ())
- {
- n++;
- mean_shortest += this_shortest;
- }
+ shortest_in_measure = shortest_in_measure <? this_shortest;
}
}
Array<Spring> springs;
Item * first_col = 0;
- for (int i= 0; i < cols.size () - 1; i++)
+ for (int i= 0; i < cols->size () - 1; i++)
{
- Item * l = dynamic_cast<Item*> (cols[i]);
+ Item * l = dynamic_cast<Item*> (cols->elem (i));
if (!first_col && Paper_column::musical_b (l))
first_col = l;
- SCM between = cols[i]->get_grob_property ("between-cols");
- if (gh_pair_p (between)
- && i > 0
- && i < cols.size ()-1
- && (gh_cdr (between) != cols[i+1]->self_scm ()
- || gh_car (between) != cols[i-1]->self_scm ())
- )
- continue ;
-
- int j = i+1;
- for (; j < cols.size () - 1; j++)
- {
- if (Paper_column::musical_b (cols[j]))
- break;
-
- SCM between = cols[j]->get_grob_property ("between-cols");
- if (!gh_pair_p (between))
- continue;
-
- if (gh_car (between) == cols[i]->self_scm () )
- break ;
- }
-
- Item * r = dynamic_cast<Item*> (cols[j]);
+ Item * r = dynamic_cast<Item*> (cols->elem (i+1));
Paper_column * lc = dynamic_cast<Paper_column*> (l);
Paper_column *rc = dynamic_cast<Paper_column*> (r);
continue ;
}
- Real note_space = note_spacing (me,lc, rc, shortest <? base_shortest_duration);
+ Real note_space = note_spacing (me,lc, rc, shortest_in_measure <? base_shortest_duration);
Real hinterfleisch = note_space;
Real headwid = gh_scm2double (me->get_grob_property ("arithmetic-multiplier"));
+
+ SCM seq = lc->get_grob_property ("spacing-sequence");
+
+ Moment dt = Paper_column::when_mom (r) - Paper_column::when_mom (l);
- for (SCM s = lc->get_grob_property ("spacing-sequence"); gh_pair_p (s); s = gh_cdr (s))
+ /*
+ hinterfleisch = hind-meat = amount of space following a note.
+
+
+ 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. */
+
+ Real stretch_distance = note_space;
+ if (shortest_in_measure <= dt)
{
- Grob *lm = unsmob_grob (gh_caar (s));
- Grob *rm = unsmob_grob (gh_cdar (s));
+ /*
+ currently SPACING-SEQUENCE is set in
+ Separating_group_spanner::find_musical_sequences (), which
+ works neatly for one-voice-per staff, however,
- // TODO; configgable.
- hinterfleisch += -headwid + Separation_item::my_width (lm)[RIGHT] -
- 0.5 * Separation_item::my_width (rm)[LEFT];
+ it can't find out the actual duration of the notes on a
+ staff, so when putting tuplets and normal patterns it gets
+ confused, (ie. believes that < { c8 c8 } { d8 d8 d8 }*2/3
+ > contains 1/12 notes. ).
+ here we kludge, by checking if the distance we're spacing
+ for is less than the shortest note.
+
+ TODO:
+
+ Move SPACING-SEQUENCE detection into a voice
+ level-engraver --or-- make sure that every column has
+ access to the note head.
- /*
- UGH: KLUDGE!
*/
-
- // if (delta_t > Moment (Rational (1,32)))
- hinterfleisch += stem_dir_correction (me, l, r);
- }
+ for (SCM s = seq; gh_pair_p (s); s = ly_cdr (s))
+ {
+ Grob *lm = unsmob_grob (ly_caar (s));
+ Grob *rm = unsmob_grob (ly_cdar (s));
+
+ // TODO; configgable.
+ hinterfleisch += -headwid + Separation_item::my_width (lm)[RIGHT] -
+ 0.5 * Separation_item::my_width (rm)[LEFT];
- Real stretch_distance = note_space - headwid;
+ }
+
+ // ? why.
+ if (gh_pair_p (seq))
+ stretch_distance -= headwid;
+ }
Spring s;
s.distance_f_ = hinterfleisch;
s.strength_f_ = 1 / stretch_distance;
Real break_dist = 0.0;
SCM espace = l->get_grob_property ("extra-space");
if (gh_pair_p (espace))
- break_dist += gh_scm2double (gh_cdr (espace));
+ break_dist += gh_scm2double (ly_cdr (espace));
if (!break_dist)
break_dist = 1.0;
// todo: naming of "distance"
espace = l->get_grob_property ("stretch-distance");
if (gh_pair_p (espace))
- break_stretch += gh_scm2double (gh_cdr (espace));
+ break_stretch += gh_scm2double (ly_cdr (espace));
if (!break_stretch)
break_stretch = 1.0;
*/
Real
New_spacing_spanner::default_bar_spacing (Grob*me, Grob *lc, Grob *rc,
- Moment shortest)
+ Moment shortest)
{
Real symbol_distance = lc->extent (lc,X_AXIS)[RIGHT] ;
Real durational_distance = 0;
Real
New_spacing_spanner::note_spacing (Grob*me, Grob *lc, Grob *rc,
- Moment shortest)
+ Moment shortest)
{
Moment shortest_playing_len = 0;
SCM s = lc->get_grob_property ("shortest-playing-duration");
- // SCM s = lc->get_grob_property ("mean-playing-duration");
+
if (unsmob_moment (s))
shortest_playing_len = *unsmob_moment (s);
shortest = 1;
}
Moment delta_t = Paper_column::when_mom (rc) - Paper_column::when_mom (lc);
- Real dist = get_duration_space (me, shortest_playing_len, shortest);
+ Real dist = 0.0;
+
+ if (delta_t.main_part_)
+ {
+ dist = get_duration_space (me, shortest_playing_len, shortest);
+ dist *= (double) (delta_t.main_part_ / shortest_playing_len.main_part_);
+ }
+ else if (delta_t.grace_part_)
+ {
+ dist = get_duration_space (me, shortest, shortest);
+ Real grace_fact = 1.0;
+ SCM gf = me->get_grob_property ("grace-space-factor");
+ if (gh_number_p (gf))
+ grace_fact = gh_scm2double (gf);
+ dist *= grace_fact;
+ }
+
+#if 0
/*
- ugh: 0.1 is an arbitrary distance.
+ TODO: figure out how to space grace notes.
*/
- dist *= (double) (delta_t.main_part_ / shortest_playing_len.main_part_)
- + 0.1 * (double) (delta_t.grace_part_ / shortest_playing_len.main_part_);
+ dist *=
+ + grace_fact * (double) (delta_t.grace_part_ / shortest_playing_len.main_part_);
Moment *lm = unsmob_moment (lc->get_grob_property ("when"));
else if (!rm->grace_part_ && lm->grace_part_)
dist *= 0.7;
}
-
+#endif
return dist;
}
-
-/**
- Correct for optical illusions. See [Wanske] p. 138. The combination
- up-stem + down-stem should get extra space, the combination
- down-stem + up-stem less.
-
- This should be more advanced, since relative heights of the note
- heads also influence required correction.
-
- Also might not work correctly in case of multi voices or staff
- changing voices
-
- TODO: lookup correction distances? More advanced correction?
- Possibly turn this off?
-
- TODO: have to check wether the stems are in the same staff.
-
- This routine reads the DIR-LIST property of both its L and R arguments. */
-Real
-New_spacing_spanner::stem_dir_correction (Grob*me, Grob*l, Grob*r)
-{
- SCM dl = l->get_grob_property ("dir-list");
- SCM dr = r->get_grob_property ("dir-list");
-
- if (scm_ilength (dl) != 1 || scm_ilength (dr) != 1)
- return 0.;
-
- dl = gh_car (dl);
- dr = gh_car (dr);
-
- assert (gh_number_p (dl) && gh_number_p (dr));
- int d1 = gh_scm2int (dl);
- int d2 = gh_scm2int (dr);
-
- if (d1 == d2)
- return 0.0;
-
-
- Real correction = 0.0;
- Real ssc = gh_scm2double (me->get_grob_property ("stem-spacing-correction"));
-
- if (d1 && d2 && d1 * d2 == -1)
- {
- correction = d1 * ssc;
- }
- else
- programming_error ("Stem directions not set correctly for optical correction");
- return correction;
-}
MAKE_SCHEME_CALLBACK (New_spacing_spanner, set_springs,1);
if (Item::breakable_b (sc))
{
Link_array<Grob> measure (all.slice (j, i+1));
- do_measure (me, measure);
+ do_measure (me, &measure);
j = i;
}
}