]> git.donarmstrong.com Git - lilypond.git/blobdiff - lily/spacing-spanner.cc
Set side-axis for InstrumentSwitch grobs
[lilypond.git] / lily / spacing-spanner.cc
index 2e174b65c22293dbe74d3b9c6a51fdbb655f1c67..8ebf100a530ad8d2085142647e9e414883fb8953 100644 (file)
@@ -3,15 +3,15 @@
 
   source file of the GNU LilyPond music typesetter
 
 
   source file of the GNU LilyPond music typesetter
 
-  (c) 1999--2006 Han-Wen Nienhuys <hanwen@xs4all.nl>
+  (c) 1999--2007 Han-Wen Nienhuys <hanwen@xs4all.nl>
 */
 
 #include "spacing-spanner.hh"
 
 #include <math.h>
 #include <cstdio>
 */
 
 #include "spacing-spanner.hh"
 
 #include <math.h>
 #include <cstdio>
-using namespace std;
 
 
+#include "spacing-options.hh"
 #include "international.hh"
 #include "main.hh"
 #include "moment.hh"
 #include "international.hh"
 #include "main.hh"
 #include "moment.hh"
@@ -20,6 +20,7 @@ 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 "spaceable-grob.hh"
 #include "spacing-interface.hh"
 #include "staff-spacing.hh"
 #include "spaceable-grob.hh"
 #include "spacing-interface.hh"
 #include "staff-spacing.hh"
@@ -27,14 +28,15 @@ using namespace std;
 #include "warn.hh"
 
 vector<Grob*>
 #include "warn.hh"
 
 vector<Grob*>
-Spacing_spanner::get_columns (Spanner *me)
+Spacing_spanner::get_columns (Grob *me_grob)
 {
 {
-  vector<Grob*> all (get_root_system (me)->columns ());
+  Spanner *me = dynamic_cast<Spanner*> (me_grob);
+  vector<Grob*> all (get_root_system (me)->used_columns ());
   vsize start = binary_search (all, (Grob*)me->get_bound (LEFT),
                               &Paper_column::less_than);
   vsize end = binary_search (all, (Grob*) me->get_bound (RIGHT),
   vsize start = binary_search (all, (Grob*)me->get_bound (LEFT),
                               &Paper_column::less_than);
   vsize end = binary_search (all, (Grob*) me->get_bound (RIGHT),
-                            &Paper_column::less_than);
-
+                            &Paper_column::less_than);  
+  
   all = vector<Grob*>::vector<Grob*> (all.begin () + start,
                                      all.begin () + end + 1);
   return all;
   all = vector<Grob*>::vector<Grob*> (all.begin () + start,
                                      all.begin () + end + 1);
   return all;
@@ -47,17 +49,16 @@ Spacing_spanner::set_springs (SCM smob)
   Spanner *me = unsmob_spanner (smob);
 
   /*
   Spanner *me = unsmob_spanner (smob);
 
   /*
-    can't use get_system() ? --hwn.
+    can't use get_system () ? --hwn.
   */
   */
-  vector<Grob*> all (get_columns (me));
-  set_explicit_neighbor_columns (all);
-
   Spacing_options options;
   options.init_from_grob (me);
   Spacing_options options;
   options.init_from_grob (me);
+  vector<Grob*> cols = Spacing_spanner::get_columns (me);
+  set_explicit_neighbor_columns (cols);
 
 
-  prune_loose_columns (me, &all, &options);
-  set_implicit_neighbor_columns (all);
-  generate_springs (me, all, &options);
+  prune_loose_columns (me, &cols, &options);
+  set_implicit_neighbor_columns (cols);
+  generate_springs (me, cols, &options);
 
   return SCM_UNSPECIFIED;
 }
 
   return SCM_UNSPECIFIED;
 }
@@ -144,9 +145,6 @@ Spacing_spanner::calc_common_shortest_duration (SCM grob)
          max_idx = i;
          max_count = counts[i];
        }
          max_idx = i;
          max_count = counts[i];
        }
-
-      // printf ("duration %d/%d, count %d\n",
-      // durations[i].num (), durations[i].den (), counts[i]);
     }
 
   SCM bsd = me->get_property ("base-shortest-duration");
     }
 
   SCM bsd = me->get_property ("base-shortest-duration");
@@ -168,15 +166,10 @@ Spacing_spanner::generate_pair_spacing (Grob *me,
 {
   if (Paper_column::is_musical (left_col))
     {
 {
   if (Paper_column::is_musical (left_col))
     {
-      bool skip_unbroken_right = false;
-
       if (!Paper_column::is_musical (right_col)
          && options->float_nonmusical_columns_
          && after_right_col
          && Paper_column::is_musical (after_right_col))
       if (!Paper_column::is_musical (right_col)
          && options->float_nonmusical_columns_
          && after_right_col
          && Paper_column::is_musical (after_right_col))
-       skip_unbroken_right = true;
-
-      if (skip_unbroken_right)
        {
          /*
            TODO: should generate rods to prevent collisions.
        {
          /*
            TODO: should generate rods to prevent collisions.
@@ -215,27 +208,105 @@ 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 *col = dynamic_cast<Paper_column *> (cols[i]);
-      Paper_column *next = (i < cols.size()-1) ? dynamic_cast<Paper_column *> (cols[i+1]) : 0;
+      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;
     }
+
+  set_column_rods (cols, 0.1); // FIXME: padding
 }
 
 /*
 }
 
 /*
-  Generate the space between two musical columns LEFT_COL and RIGHT_COL, given
-  spacing parameters INCR and SHORTEST.
+  Generate the space between two musical columns LEFT_COL and RIGHT_COL.
 */
 void
 Spacing_spanner::musical_column_spacing (Grob *me,
 */
 void
 Spacing_spanner::musical_column_spacing (Grob *me,
@@ -243,45 +314,22 @@ Spacing_spanner::musical_column_spacing (Grob *me,
                                         Item *right_col,
                                         Spacing_options const *options)
 {
                                         Item *right_col,
                                         Spacing_options const *options)
 {
-  bool expand_only = false;
-  Real base_note_space = note_spacing (me, left_col, right_col, options, &expand_only);
-
-  Real max_fixed = 0;
-  Real max_space = 0;
-  Real compound_note_space = 0.0;
-  Real compound_fixed_note_space = 0.0;
+  Real base_note_space = note_spacing (me, left_col, right_col, options);
+  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;
-      
+      vector<Spring> springs;
       extract_grob_set (left_col, "right-neighbors", neighbors);
 
       extract_grob_set (left_col, "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 (vsize i = 0; i < neighbors.size (); i++)
        {
          Grob *wish = neighbors[i];
 
       for (vsize i = 0; i < neighbors.size (); i++)
        {
          Grob *wish = neighbors[i];
 
-         Item *wish_rcol = Note_spacing::right_column (wish);
-         if (Note_spacing::left_column (wish) != left_col
+         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;
 
              || (wish_rcol != right_col && wish_rcol != right_col->original ()))
            continue;
 
@@ -290,40 +338,31 @@ Spacing_spanner::musical_column_spacing (Grob *me,
            music.  */
          if (Note_spacing::has_interface (wish))
            {
            music.  */
          if (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))
            {
-             Real left_col_stick_out = robust_relative_extent (left_col, left_col,  X_AXIS)[RIGHT];
-             compound_fixed_note_space = max (left_col_stick_out, options->increment_);
-
-             compound_note_space = max (base_note_space,
-                                        base_note_space - options->increment_ + left_col_stick_out);
+             /*
+               There used to be code that examined left_col->extent
+               (X_AXIS), but this is resulted in unexpected wide
+               spacing, because the width of s^"text" output is also
+               taken into account here.
+              */
+             spring = Spring (max (base_note_space, options->increment_),
+                              options->increment_);
            }
          else
            {
            }
          else
            {
@@ -331,44 +370,30 @@ Spacing_spanner::musical_column_spacing (Grob *me,
                Fixed should be 0.0. If there are no spacing wishes, we're
                likely dealing with polyphonic spacing of hemiolas.
            
                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_
+               We used to have min_distance_ = options->increment_
 
                but this can lead to numeric instability problems when we
                do
            
 
                but this can lead to numeric instability problems when we
                do
            
-               inverse_strength = (compound_note_space - compound_fixed_note_space)
+               inverse_strength = (distance_ - min_distance_)
       
              */
       
              */
-
-             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.
   */
@@ -383,16 +408,47 @@ Spacing_spanner::musical_column_spacing (Grob *me,
        pack as much bars of music as possible into a line, but the
        line will then be stretched to fill the whole linewidth.
       */
        pack as much bars of music as possible into a line, but the
        line will then be stretched to fill the whole linewidth.
       */
-      inverse_strength = 1.0;
-      distance = compound_fixed_note_space;
-    }
-  else
-    {
-      inverse_strength = (compound_note_space - compound_fixed_note_space);
-      distance = compound_note_space;
+      spring.set_distance (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);
+}
+
+/*
+  Check if COL fills the whole measure.
+ */
+bool
+Spacing_spanner::fills_measure (Grob *me, Item *left, Item *col)
+{
+  System *sys = get_root_system (me);
+  Item *next = sys->column (col->get_column ()->get_rank () + 1);
+  if (!next)
+    return false;
+
+  if (Paper_column::is_musical (next)
+      || Paper_column::is_musical (left)
+      || !Paper_column::is_musical (col)
+      || !Paper_column::is_used (next))
+    return false;
+  
+  Moment dt =
+    Paper_column::when_mom (next) - Paper_column::when_mom (col);
+  
+  Moment *len = unsmob_moment (left->get_property ("measure-length"));
+  if (!len)
+    return false;
+  
+  /*
+    Don't check for exact measure length, since ending measures are
+    often shortened due to pickups.
+   */
+  if (dt.main_part_ > len->main_part_ / Rational (2)
+      && (next->is_broken ()
+         || next->break_status_dir ()))
+    return true;
+
+  return false;
 }
 
 /*
 }
 
 /*
@@ -402,12 +458,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);
 
@@ -422,75 +474,46 @@ Spacing_spanner::breakable_column_spacing (Grob *me, Item *l, Item *r,
          if (!spacing_grob || !Staff_spacing::has_interface (spacing_grob))
            continue;
 
          if (!spacing_grob || !Staff_spacing::has_interface (spacing_grob))
            continue;
 
-         Real space;
-         Real fixed_space;
-
          /*
            column for the left one settings should be ok due automatic
            pointer munging.
          /*
            column for the left one settings should be ok due automatic
            pointer munging.
-
          */
          assert (spacing_grob->get_column () == l);
 
          */
          assert (spacing_grob->get_column () == l);
 
-         Staff_spacing::get_spacing_params (spacing_grob,
-                                            &space, &fixed_space);
-
-         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_space);
-         
-         compound_space += space;
-         compound_fixed += fixed_space;
-         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))
+    {
+      spring.set_distance (spring.distance () + 1.0);
+      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);
-
-  /*
-    There used to be code that changed spacing depending on
-    raggedright setting.  Ugh.
-
-    Do it more cleanly, or rename the property.
+    {
+      spring.set_min_distance (0.0);
+      spring.set_default_strength ();
+    }
 
 
-  */
-  Real inverse_strength = (compound_space - compound_fixed);
-  Real distance = compound_space;
-  Spaceable_grob::add_spring (l, r, distance, inverse_strength);
+  Spaceable_grob::add_spring (l, r, spring);
 }
 
 ADD_INTERFACE (Spacing_spanner,
 }
 
 ADD_INTERFACE (Spacing_spanner,
@@ -518,9 +541,3 @@ ADD_INTERFACE (Spacing_spanner,
               
               );
 
               
               );
 
-ADD_INTERFACE (Spacing_interface,
-              "Something to do with line breaking and spacing. "
-              "Kill this one after determining line breaks.",
-              
-              "");
-