]> git.donarmstrong.com Git - lilypond.git/blobdiff - lily/spacing-spanner.cc
* Documentation/user/refman.itely,
[lilypond.git] / lily / spacing-spanner.cc
index 5e48941547d3a43869b5e65c23a090d85ceb0905..5a889c74b480154d3e452a64e75c7ddc9f278d77 100644 (file)
@@ -3,14 +3,16 @@
   
   source file of the GNU LilyPond music typesetter
   
-  (c) 1999--2002 Han-Wen Nienhuys <hanwen@cs.uu.nl>
+  (c) 1999--2003 Han-Wen Nienhuys <hanwen@cs.uu.nl>
   
  */
 
 #include <math.h>
 #include <stdio.h>
 
+#include "main.hh"
 #include "system.hh"
+#include "warn.hh"
 #include "paper-def.hh"
 #include "paper-score.hh"
 #include "paper-column.hh"
 #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:
@@ -42,10 +38,10 @@ public:
   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<Grob> const &);  
+  static Rational find_shortest (Grob *, Link_array<Grob> 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<Grob> *cols, Rational);
+  static void prune_loose_columns (Grob*,Link_array<Grob> *cols, Rational);
   static void find_loose_columns (Link_array<Grob> cols);
   static void set_explicit_neighbor_columns (Link_array<Grob> cols);
   static void set_implicit_neighbor_columns (Link_array<Grob> cols);
@@ -58,6 +54,13 @@ public:
 /*
   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) 
@@ -95,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<Item*> (Note_spacing::right_column  (r_neighbor));
 
   if (l == l_neighbor && r == r_neighbor)
@@ -104,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;
 }
 
 /*
@@ -132,7 +154,7 @@ loose_column (Grob *l, Grob *c, Grob *r)
   between.
 */
 void
-Spacing_spanner::prune_loose_colunms (Grob*me,Link_array<Grob> *cols, Rational shortest)
+Spacing_spanner::prune_loose_columns (Grob*me,Link_array<Grob> *cols, Rational shortest)
 {
   Link_array<Grob> newcols;
   Real increment = gh_scm2double (me->get_grob_property ("spacing-increment"));
@@ -216,7 +238,7 @@ Spacing_spanner::prune_loose_colunms (Grob*me,Link_array<Grob> *cols, Rational s
          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<Item*> (cols->elem(i-1));
          r.item_l_drul_[RIGHT] = dynamic_cast<Item*> (cols->elem (i+1));
 
@@ -248,7 +270,7 @@ Spacing_spanner::set_explicit_neighbor_columns (Link_array<Grob> cols)
        {
          Item * wish = dynamic_cast<Item*> (unsmob_grob (gh_car (s)));
 
-         Item * lc = wish->column_l ();
+         Item * lc = wish->get_column ();
          Grob * right = Note_spacing::right_column (wish);
 
          if (!right)
@@ -256,8 +278,8 @@ Spacing_spanner::set_explicit_neighbor_columns (Link_array<Grob> cols)
 
          Item * rc = dynamic_cast<Item*> (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.
@@ -280,7 +302,7 @@ Spacing_spanner::set_explicit_neighbor_columns (Link_array<Grob> cols)
              && unsmob_grob (gh_car (left_neighs)))
            {
              Item * it = dynamic_cast<Item*> (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)
@@ -339,12 +361,25 @@ Spacing_spanner::set_springs (SCM smob)
 {
   Grob *me = unsmob_grob (smob);
 
-  Link_array<Grob> all (me->pscore_l_->line_l_->column_l_arr ()) ;
+  Link_array<Grob> all (me->pscore_->system_->columns ()) ;
 
   set_explicit_neighbor_columns (all);
 
-  Rational global_shortest = find_shortest (all);
-  prune_loose_colunms (me, &all, global_shortest);
+  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_columns (me, &all, global_shortest);
   set_implicit_neighbor_columns (all);
 
   
@@ -375,7 +410,7 @@ Spacing_spanner::set_springs (SCM smob)
 
 */
 Rational
-Spacing_spanner::find_shortest (Link_array<Grob> const &cols)
+Spacing_spanner::find_shortest (Grob *me, Link_array<Grob> const &cols)
 {
   /*
     ascending in duration
@@ -445,10 +480,11 @@ Spacing_spanner::find_shortest (Link_array<Grob> const &cols)
       //      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 <? durations[max_idx] ;
 
@@ -508,7 +544,8 @@ Spacing_spanner::do_measure (Rational shortest, Grob*me, Link_array<Grob> *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)
@@ -532,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;
 
       /*
@@ -549,25 +586,90 @@ Spacing_spanner::musical_column_spacing (Grob *me, Item * lc, Item *rc, Real inc
        }
     }
 
+  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;
+    }
+
+  /*
+    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 <? max_note_space;
+
+  bool packed = to_boolean (me->get_paper ()->get_scmvar ("packed"));
+  Real strength, distance;
+
+  /*
+    TODO: make sure that the space doesn't exceed the right margin.
+   */
+  if (packed)
+    {
+      /*
+       In packed mode, pack notes as tight as possible.  This makes
+       sense mostly in combination with raggedright mode: the notes
+       are then printed at minimum distance.  This is mostly useful
+       for ancient notation, but may also be useful for some flavours
+       of contemporary music.  If not in raggedright mode, lily will
+       pack as much bars of music as possible into a line, but the
+       line will then be stretched to fill the whole linewidth.
+      */
+      strength = 1.0;
+      distance = max_fixed_note_space;
+    }
+  else
+    {
+      strength = 1 / (max_note_space - max_fixed_note_space);
+      distance = max_note_space;
     }
 
-  bool ragged = to_boolean (me->paper_l ()->get_scmvar ("raggedright"));
-  Real strength = (ragged) ? 1.0 : 1 / (max_note_space - max_fixed_note_space);
-  Real distance = (ragged) ? max_fixed_note_space : max_note_space;
-  Spaceable_grob::add_spring (lc, rc, distance, strength, expand_only);
+  //  Spaceable_grob::add_spring (lc, rc, distance, strength, expand_only);
+
+  Spaceable_grob::add_spring (lc, rc, distance, strength, false);  
 }
 
+
+/*
+  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 = l->extent (l, X_AXIS)[RIGHT] - r->extent (r, X_AXIS)[LEFT];
-      
+  *fixed = 0.0;
+  Direction d = LEFT;
+  Drul_array<Item*> 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"));
@@ -586,8 +688,6 @@ Spacing_spanner::standard_breakable_column_spacing (Grob * me, Item*l, Item*r,
 
       *space = *fixed + get_duration_space (me, dt, shortest.main_part_, &dummy);
     }
-  
-  
 }
 
 
@@ -619,10 +719,20 @@ Spacing_spanner::breakable_column_spacing (Grob*me, Item* l, Item *r,Moment shor
        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;
@@ -632,10 +742,21 @@ Spacing_spanner::breakable_column_spacing (Grob*me, Item* l, Item *r,Moment shor
 
   
   
-
+    
   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.)
+    */      
+      warning ("No spacing wishes found. Does your score have a staff?");
       max_space = 2.0;
       max_fixed = 1.0;
     }
@@ -652,10 +773,16 @@ Spacing_spanner::breakable_column_spacing (Grob*me, Item* l, Item *r,Moment shor
     Hmm.  we do 1/0 in the next thing. Perhaps we should check if this
     works on all architectures.
    */
-  
-  bool ragged = to_boolean (me->paper_l ()->get_scmvar ("raggedright"));
-  Real strength = (ragged) ? 1.0 : 1 / (max_space - max_fixed);
-  Real distance = (ragged) ? max_fixed : max_space;
+
+  /*
+    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);
 }
 
@@ -688,8 +815,10 @@ Spacing_spanner::get_duration_space (Grob*me, Moment d, Rational shortest, bool
 
        */
       Rational ratio = d.main_part_ / shortest;
-      
+
+#if 0
       *expand_only = true;
+#endif
       return ((k-1) + double (ratio)) * incr;
     }
   else
@@ -720,7 +849,7 @@ 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;
     }
 
@@ -730,6 +859,14 @@ Spacing_spanner::note_spacing (Grob*me, Grob *lc, Grob *rc,
   Moment delta_t = rwhen - lwhen;
   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 = 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);
@@ -757,17 +894,21 @@ Spacing_spanner::note_spacing (Grob*me, Grob *lc, Grob *rc,
 
 
 ADD_INTERFACE (Spacing_spanner,"spacing-spanner-interface",
-  "
-The space taken by a note is dependent on its duration. Doubling a
-duration adds spacing-increment to the space. The most common shortest
-note gets shortest-duration-space. Notes that are even shorter are
-spaced proportonial to their duration.
-
-Typically, the increment is the width of a black note head.  In a
-piece with lots of 8th notes, and some 16th notes, the eighth note
-gets 2 note heads width (i.e. the space following a note is 1 note
-head width) A 16th note is followed by 0.5 note head width. The
-quarter note is followed by  3 NHW, the half by 4 NHW, etc.
-",
-  "grace-space-factor spacing-increment shortest-duration-space");
+"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.",
+  "");