]> git.donarmstrong.com Git - lilypond.git/blobdiff - lily/spacing-spanner.cc
Rename Font_metric::get_indexed_char() to
[lilypond.git] / lily / spacing-spanner.cc
index 69b0de980bd635c840d6625c4d8bb41de03ce550..6ec8108d691decc8040ba297046c395e6650aeb9 100644 (file)
@@ -1,9 +1,20 @@
 /*
 /*
-  spacing-spanner.cc -- implement Spacing_spanner
+  This file is part of LilyPond, the GNU music typesetter.
 
 
-  source file of the GNU LilyPond music typesetter
+  Copyright (C) 1999--2010 Han-Wen Nienhuys <hanwen@xs4all.nl>
 
 
-  (c) 1999--2007 Han-Wen Nienhuys <hanwen@xs4all.nl>
+  LilyPond is free software: you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation, either version 3 of the License, or
+  (at your option) any later version.
+
+  LilyPond is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with LilyPond.  If not, see <http://www.gnu.org/licenses/>.
 */
 
 #include "spacing-spanner.hh"
 */
 
 #include "spacing-spanner.hh"
@@ -11,8 +22,6 @@
 #include <math.h>
 #include <cstdio>
 
 #include <math.h>
 #include <cstdio>
 
-using namespace std;
-
 #include "spacing-options.hh"
 #include "international.hh"
 #include "main.hh"
 #include "spacing-options.hh"
 #include "international.hh"
 #include "main.hh"
@@ -22,6 +31,8 @@ using namespace std;
 #include "paper-column.hh"
 #include "paper-score.hh"
 #include "pointer-group-interface.hh"
 #include "paper-column.hh"
 #include "paper-score.hh"
 #include "pointer-group-interface.hh"
+#include "separation-item.hh"
+#include "skyline-pair.hh"
 #include "spaceable-grob.hh"
 #include "spacing-interface.hh"
 #include "staff-spacing.hh"
 #include "spaceable-grob.hh"
 #include "spacing-interface.hh"
 #include "staff-spacing.hh"
@@ -38,8 +49,8 @@ Spacing_spanner::get_columns (Grob *me_grob)
   vsize end = binary_search (all, (Grob*) me->get_bound (RIGHT),
                             &Paper_column::less_than);  
   
   vsize end = binary_search (all, (Grob*) me->get_bound (RIGHT),
                             &Paper_column::less_than);  
   
-  all = vector<Grob*>::vector<Grob*> (all.begin () + start,
-                                     all.begin () + end + 1);
+  all = vector<Grob*> (all.begin () + start,
+                      all.begin () + end + 1);
   return all;
 }
 
   return all;
 }
 
@@ -137,7 +148,7 @@ Spacing_spanner::calc_common_shortest_duration (SCM grob)
        }
     }
 
        }
     }
 
-  int max_idx = -1;
+  vsize max_idx = VPOS;
   int max_count = 0;
   for (vsize i = durations.size (); i--;)
     {
   int max_count = 0;
   for (vsize i = durations.size (); i--;)
     {
@@ -153,7 +164,7 @@ Spacing_spanner::calc_common_shortest_duration (SCM grob)
   if (Moment *m = unsmob_moment (bsd))
     d = m->main_part_;
 
   if (Moment *m = unsmob_moment (bsd))
     d = m->main_part_;
 
-  if (max_idx >= 0)
+  if (max_idx != VPOS)
     d = min (d, durations[max_idx]);
 
   return Moment (d).smobbed_copy ();
     d = min (d, durations[max_idx]);
 
   return Moment (d).smobbed_copy ();
@@ -168,7 +179,7 @@ Spacing_spanner::generate_pair_spacing (Grob *me,
   if (Paper_column::is_musical (left_col))
     {
       if (!Paper_column::is_musical (right_col)
   if (Paper_column::is_musical (left_col))
     {
       if (!Paper_column::is_musical (right_col)
-         && options->float_nonmusical_columns_
+         && (options->float_nonmusical_columns_ || to_boolean (right_col->get_property ("maybe-loose")))
          && after_right_col
          && Paper_column::is_musical (after_right_col))
        {
          && after_right_col
          && Paper_column::is_musical (after_right_col))
        {
@@ -209,22 +220,102 @@ Spacing_spanner::generate_pair_spacing (Grob *me,
     }
 }
 
     }
 }
 
+static void
+set_column_rods (vector<Grob*> const &cols, Real padding)
+{
+  /* distances[i] will be the minimum distance between column i and column i+1 */
+  vector<Real> distances;
+
+  for (vsize i = 1; i < cols.size (); i++)
+    {
+      assert (distances.size () == i-1);
+
+      Item *r = dynamic_cast<Item*> (cols[i]);
+      Item *rb = r->find_prebroken_piece (LEFT);
+
+      if (Separation_item::is_empty (r) && (!rb || Separation_item::is_empty (rb)))
+       {
+         distances.push_back (0);
+         continue;
+       }
+
+      Skyline_pair *skys = Skyline_pair::unsmob (r->get_property ("horizontal-skylines"));
+      Real right_stickout = skys ? (*skys)[LEFT].max_height () : 0.0;
+
+      /* min rather than max because right-stickout will be negative if the right-hand column
+        sticks out a lot to the left */
+      right_stickout = min (right_stickout,
+                           Separation_item::conditional_skyline (r, cols[i-1]).max_height ());
+
+      Drul_array<Item*> r_cols (r, rb);
+      Drul_array<Real> cur_dist (0.0, 0.0);
+
+      /* This is an inner loop and hence it is potentially quadratic. However, we only continue
+        as long as there is a rod to insert. Therefore, this loop will usually only execute
+        a constant number of times per iteration of the outer loop. */
+      for (vsize j = i; j--;)
+       {
+         Item *l = dynamic_cast<Item*> (cols[j]);
+         Item *lb = l->find_prebroken_piece (RIGHT);
+         Skyline_pair *skys = Skyline_pair::unsmob (l->get_property ("horizontal-skylines"));
+         Real left_stickout = skys ? (*skys)[RIGHT].max_height () : 0.0;
+         bool done = true;
+
+         Direction d = LEFT;
+         do
+           {
+             if (j < i-1)
+               cur_dist[d] += distances[j];
+
+             Item *r_col = r_cols[d];
+             bool touches = right_stickout - left_stickout + cur_dist[d] < 0.0;
+             Real dist = 0.0;
+
+             /* we set a distance for the line-starter column even if it's non-broken counterpart
+                doesn't touch the right column. */
+             if (lb)
+               Separation_item::set_distance (lb, r_col, padding);
+
+             if (touches || j == i-1)
+               dist = Separation_item::set_distance (l, r_col, padding);
+
+             if (j == i-1 && d == LEFT)
+               distances.push_back (dist);
+
+             if (j == i-1)
+               cur_dist[d] = distances[j];
+
+             done = done && !touches;
+           }
+         while (flip (&d) != LEFT && rb);
+
+         /* we need the empty check for gregorian notation, where there are a lot of
+            extraneous paper-columns that we need to skip over */
+         if (done && !Separation_item::is_empty (l))
+           break;
+       }
+    }
+}
+
+
 void
 Spacing_spanner::generate_springs (Grob *me,
                                   vector<Grob*> const &cols,
                                   Spacing_options const *options)
 {
 void
 Spacing_spanner::generate_springs (Grob *me,
                                   vector<Grob*> const &cols,
                                   Spacing_options const *options)
 {
-  Paper_column *prev = 0;
-  for (vsize i = 0; i < cols.size (); i++)
+  Paper_column *prev = dynamic_cast<Paper_column*> (cols[0]);
+  for (vsize i = 1; i < cols.size (); i++)
     {
       Paper_column *col = dynamic_cast<Paper_column *> (cols[i]);
       Paper_column *next = (i + 1 < cols.size ()) ? dynamic_cast<Paper_column *> (cols[i+1]) : 0;
       
     {
       Paper_column *col = dynamic_cast<Paper_column *> (cols[i]);
       Paper_column *next = (i + 1 < cols.size ()) ? dynamic_cast<Paper_column *> (cols[i+1]) : 0;
       
-      if (i > 0)
-       generate_pair_spacing (me, prev, col, next, options);
+      generate_pair_spacing (me, prev, col, next, options);
 
       prev = col;
     }
 
       prev = col;
     }
+
+  Real padding = robust_scm2double (prev->get_property ("padding"), 0.1);
+  set_column_rods (cols, padding);
 }
 
 /*
 }
 
 /*
@@ -237,76 +328,54 @@ Spacing_spanner::musical_column_spacing (Grob *me,
                                         Spacing_options const *options)
 {
   Real base_note_space = note_spacing (me, left_col, right_col, options);
                                         Spacing_options const *options)
 {
   Real base_note_space = note_spacing (me, left_col, right_col, options);
-
-  Real max_fixed = 0;
-  Real max_space = 0;
-  Real compound_note_space = 0.0;
-  Real compound_fixed_note_space = 0.0;
+  Spring spring;
 
   if (options->stretch_uniformly_)
 
   if (options->stretch_uniformly_)
-    {
-      compound_note_space = base_note_space;
-            
-      if (!Paper_column::is_musical (right_col))
-       {
-         /*
-           Crude fix for notes that lead up to barlines and time sigs.
-         */
-         Interval lext = right_col->extent (right_col, X_AXIS);
-         if (!lext.is_empty ())
-           compound_note_space += -lext[LEFT];
-       }
-    }
+    spring = Spring (base_note_space, 0.0);
   else
     {
   else
     {
-      int wish_count = 0;
-      
-      extract_grob_set (left_col, "right-neighbors", neighbors);
+      vector<Spring> springs;
+      extract_grob_set (left_col, "spacing-wishes", wishes);
 
 
-      /*
-       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 (vsize i = 0; i < neighbors.size (); i++)
+      for (vsize i = 0; i < wishes.size (); i++)
        {
        {
-         Grob *wish = neighbors[i];
+         Grob *wish = wishes[i];
+         if (Spacing_interface::left_column (wish) != left_col)
+           {
+             /* This shouldn't really happen, but the ancient music
+                stuff really messes up the spacing code, grrr
+             */
+             continue;
+           }
 
 
-         Item *wish_rcol = Spacing_interface::right_column (wish);
-         if (Spacing_interface::left_column (wish) != left_col
-             || (wish_rcol != right_col && wish_rcol != right_col->original ()))
-           continue;
+         extract_grob_set (wish, "right-items", right_items);
+         bool found_matching_column = false;
+         for (vsize j = 0; j < right_items.size (); j++)
+           {
+             Item *it = dynamic_cast<Item*> (right_items[j]);
+             if (it && (right_col == it->get_column ()
+                        || right_col->original () == it->get_column ()))
+               found_matching_column = true;
+           }
 
          /*
            This is probably a waste of time in the case of polyphonic
            music.  */
 
          /*
            This is probably a waste of time in the case of polyphonic
            music.  */
-         if (Note_spacing::has_interface (wish))
+         if (found_matching_column && Note_spacing::has_interface (wish))
            {
            {
-             Real space = 0.0;
-             Real fixed = 0.0;
-
-             Note_spacing::get_spacing (wish, right_col, base_note_space, options->increment_, &space, &fixed);
-
-
-             max_space = max (max_space, space);
-             max_fixed = max (max_fixed, fixed);
-             
-             compound_note_space += space;
-             compound_fixed_note_space += fixed;
-             wish_count++;
+             Real inc = options->increment_;
+             Grob *gsp = unsmob_grob (left_col->get_object ("grace-spacing"));
+             if (gsp && Paper_column::when_mom (left_col).grace_part_)
+               {
+                 Spacing_options grace_opts;
+                 grace_opts.init_from_grob (gsp);
+                 inc = grace_opts.increment_;
+               }
+             springs.push_back (Note_spacing::get_spacing (wish, right_col, base_note_space, inc));
            }
        }
 
            }
        }
 
-      if (Paper_column::when_mom (right_col).grace_part_
-         && !Paper_column::when_mom (left_col).grace_part_)
-       {
-         /*
-           Ugh. 0.8 is arbitrary.
-         */
-         compound_note_space *= 0.8;
-       }
-
-      if (compound_note_space < 0 || wish_count == 0)
+      if (springs.empty ())
        {
 
          if (!Paper_column::is_musical (right_col))
        {
 
          if (!Paper_column::is_musical (right_col))
@@ -317,54 +386,32 @@ Spacing_spanner::musical_column_spacing (Grob *me,
                spacing, because the width of s^"text" output is also
                taken into account here.
               */
                spacing, because the width of s^"text" output is also
                taken into account here.
               */
-             compound_fixed_note_space = options->increment_;
-             compound_note_space = max (base_note_space,
-                                        options->increment_);
+             spring = Spring (max (base_note_space, options->increment_),
+                              options->increment_);
            }
          else
            {
              /*
            }
          else
            {
              /*
-               Fixed should be 0.0. If there are no spacing wishes, we're
-               likely dealing with polyphonic spacing of hemiolas.
-           
-               We used to have compound_fixed_note_space = options->increment_
-
-               but this can lead to numeric instability problems when we
-               do
-           
-               inverse_strength = (compound_note_space - compound_fixed_note_space)
-      
+               Min distance should be 0.0. If there are no spacing
+               wishes, we're probably dealing with polyphonic spacing
+               of hemiolas.      
              */
              */
-
-             compound_note_space = base_note_space;
-             compound_fixed_note_space = 0.0;
+             spring = Spring (base_note_space, 0.0);
            }
        }
            }
        }
-      else if (to_boolean (me->get_property ("average-spacing-wishes")))
-       {
-         compound_note_space /= wish_count;
-         compound_fixed_note_space /= wish_count;
-       }
       else
       else
-       {
-         compound_fixed_note_space = max_fixed;
-         compound_note_space = max_space;
-       }
+       spring = merge_springs (springs);
+    }
 
 
+  if (Paper_column::when_mom (right_col).grace_part_
+      && !Paper_column::when_mom (left_col).grace_part_)
+    {
       /*
       /*
-       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?
+       Ugh. 0.8 is arbitrary.
       */
       */
-      compound_fixed_note_space = min (compound_fixed_note_space,
-                                      compound_note_space);
+      spring *= 0.8;
     }
 
     }
 
-  Real inverse_strength = 1.0;
-  Real distance = 1.0;
-
   /*
     TODO: make sure that the space doesn't exceed the right margin.
   */
   /*
     TODO: make sure that the space doesn't exceed the right margin.
   */
@@ -372,23 +419,34 @@ Spacing_spanner::musical_column_spacing (Grob *me,
     {
       /*
        In packed mode, pack notes as tight as possible.  This makes
     {
       /*
        In packed mode, pack notes as tight as possible.  This makes
-       sense mostly in combination with raggedright mode: the notes
+       sense mostly in combination with ragged-right mode: the notes
        are then printed at minimum distance.  This is mostly useful
        for ancient notation, but may also be useful for some flavours
        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
+       of contemporary music.  If not in ragged-right mode, lily will
+       pack as many bars of music as possible into a line, but the
        line will then be stretched to fill the whole linewidth.
        line will then be stretched to fill the whole linewidth.
+
+       Note that we don't actually pack things as tightly as possible:
+       we don't allow the next column to begin before this one ends.
       */
       */
-      inverse_strength = 1.0;
-      distance = compound_fixed_note_space;
-    }
-  else
-    {
-      inverse_strength = (compound_note_space - compound_fixed_note_space);
-      distance = compound_note_space;
+      /* FIXME: the else clause below is the "right" thing to do,
+        but we can't do it because of all the empty columns that the
+        ligature-engravers leave lying around. In that case, the extent of
+        the column is incorrect because it includes note-heads that aren't
+        there. We get around this by only including the column extent if
+        the left-hand column is "genuine". This is a dirty hack and it
+        should be fixed in the ligature-engravers. --jneem
+      */
+      if (Paper_column::is_extraneous_column_from_ligature (left_col))
+       spring.set_distance (spring.min_distance ());
+      else
+       spring.set_distance (max (left_col->extent (left_col, X_AXIS)[RIGHT],
+                                 spring.min_distance ()));
+
+      spring.set_inverse_stretch_strength (1.0);
     }
 
     }
 
-  Spaceable_grob::add_spring (left_col, right_col, distance, inverse_strength);
+  Spaceable_grob::add_spring (left_col, right_col, spring);
 }
 
 /*
 }
 
 /*
@@ -434,12 +492,8 @@ void
 Spacing_spanner::breakable_column_spacing (Grob *me, Item *l, Item *r,
                                           Spacing_options const *options)
 {
 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;
-  Real max_fixed = 0.0;
-  Real max_space = 0.0;
-  
-  int wish_count = 0;
+  vector<Spring> springs;
+  Spring spring;
 
   Moment dt = Paper_column::when_mom (r) - Paper_column::when_mom (l);
 
 
   Moment dt = Paper_column::when_mom (r) - Paper_column::when_mom (l);
 
@@ -460,82 +514,58 @@ Spacing_spanner::breakable_column_spacing (Grob *me, Item *l, Item *r,
          */
          assert (spacing_grob->get_column () == l);
 
          */
          assert (spacing_grob->get_column () == l);
 
-         Spring sp = Staff_spacing::get_spacing_params (spacing_grob);
-         Real space = sp.distance_;
-         Real fixed = sp.distance_ - sp.inverse_compress_strength_;
-
-         if (Paper_column::when_mom (r).grace_part_)
-           {
-             /*
-               Correct for grace notes.
-
-               Ugh. The 0.8 is arbitrary.
-             */
-             space *= 0.8;
-           }
-
-         max_space = max (max_space, space);
-         max_fixed = max (max_fixed, fixed);
-         
-         compound_space += space;
-         compound_fixed += fixed;
-         wish_count++;
+         springs.push_back (Staff_spacing::get_spacing (spacing_grob, r));
        }
     }
 
        }
     }
 
-  if (compound_space <= 0.0 || !wish_count)
-    {
-      standard_breakable_column_spacing (me, l, r, &compound_fixed, &compound_space,
-                                        options);
-      wish_count = 1;
-    }
+  if (springs.empty ())
+    spring = standard_breakable_column_spacing (me, l, r, options);
   else
   else
+    spring = merge_springs (springs);
+
+  if (Paper_column::when_mom (r).grace_part_)
     {
     {
-      if (to_boolean (me->get_property ("average-spacing-wishes")))
-       {
-         compound_space /= wish_count;
-         compound_fixed /= wish_count;
-       }
-      else
-       {
-         compound_fixed = max_fixed;
-         compound_space = max_space;
-       }
-      
+      /*
+       Correct for grace notes.
+       
+       Ugh. The 0.8 is arbitrary.
+      */
+      spring *= 0.8;
     }
 
   if (Paper_column::is_musical (r)
       && l->break_status_dir () == CENTER
       && fills_measure (me, l, r))
     {
     }
 
   if (Paper_column::is_musical (r)
       && l->break_status_dir () == CENTER
       && fills_measure (me, l, r))
     {
-      compound_space += 1.0; 
+      Real full_measure_extra_space = robust_scm2double (l->get_property ("full-measure-extra-space"), 1.0);
+      spring.set_distance (spring.distance () + full_measure_extra_space);
+      spring.set_default_strength ();
     }
   
   if (options->stretch_uniformly_ && l->break_status_dir () != RIGHT)
     }
   
   if (options->stretch_uniformly_ && l->break_status_dir () != RIGHT)
-    compound_fixed = 0.0;
-
-  assert (!isinf (compound_space));
-  compound_space = max (compound_space, compound_fixed);
-
-  Real inverse_strength = (compound_space - compound_fixed);
-  Real distance = compound_space;
+    {
+      spring.set_min_distance (0.0);
+      spring.set_default_strength ();
+    }
 
 
-  Spaceable_grob::add_spring (l, r, distance, inverse_strength);
+  Spaceable_grob::add_spring (l, r, spring);
 }
 
 ADD_INTERFACE (Spacing_spanner,
 }
 
 ADD_INTERFACE (Spacing_spanner,
-              "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 @code{shortest-duration-space}. Notes that are even shorter are\n"
-              "spaced proportonial to their duration.\n"
+              "The space taken by a note is dependent on its duration."
+              "  Doubling a duration adds @code{spacing-increment} to the"
+              " space.  The most common shortest note gets"
+              " @code{shortest-duration-space}.  Notes that are even shorter"
+              " are spaced proportonial to their duration.\n"
               "\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",
-
-              
+              "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 a 2@tie{}note heads width (i.e., the space"
+              " following a note is a 1@tie{}note head width).  A 16th note"
+              " is followed by 0.5 note head width.  The quarter note is"
+              " followed by 3@tie{}NHW, the half by 4@tie{}NHW, etc.",
+
+              /* properties */
               "average-spacing-wishes "
               "base-shortest-duration "
               "common-shortest-duration "
               "average-spacing-wishes "
               "base-shortest-duration "
               "common-shortest-duration "
@@ -545,6 +575,5 @@ ADD_INTERFACE (Spacing_spanner,
               "strict-grace-spacing "
               "strict-note-spacing "
               "uniform-stretching "
               "strict-grace-spacing "
               "strict-note-spacing "
               "uniform-stretching "
-              
               );
 
               );