From d903c7971c00e582b88ef6b76b97573186d3eb76 Mon Sep 17 00:00:00 2001
From: Joe Neeman <joeneeman@gmail.com>
Date: Tue, 19 Jun 2007 10:38:23 +1000
Subject: [PATCH] use springs instead of fixed/distance pairs

---
 lily/include/note-spacing.hh            |   3 +-
 lily/include/spaceable-grob.hh          |   2 +
 lily/include/spring.hh                  |  21 ++++-
 lily/include/staff-spacing.hh           |   2 +-
 lily/note-spacing.cc                    |  22 ++---
 lily/paper-column.cc                    |   6 +-
 lily/spaceable-grob.cc                  |  35 +++-----
 lily/spacing-determine-loose-columns.cc |  15 +---
 lily/spacing-interface.cc               |   2 +-
 lily/spacing-spanner.cc                 | 110 +++++-------------------
 lily/spring-smob.cc                     |   9 --
 lily/staff-spacing.cc                   |   8 +-
 12 files changed, 80 insertions(+), 155 deletions(-)

diff --git a/lily/include/note-spacing.hh b/lily/include/note-spacing.hh
index dcc9b2fe5a..a14c43c8b7 100644
--- a/lily/include/note-spacing.hh
+++ b/lily/include/note-spacing.hh
@@ -11,13 +11,14 @@
 
 #include "grob-interface.hh"
 #include "lily-proto.hh"
+#include "spring.hh"
 
 class Note_spacing
 {
 public:
   DECLARE_GROB_INTERFACE();
 
-  static void get_spacing (Grob *me, Item *, Real, Real, Real *, Real *);
+  static Spring get_spacing (Grob *me, Item *, Real, Real);
   static void stem_dir_correction (Grob *me, Item *next_col, Real incr,
 				   Real *, Real *);
 };
diff --git a/lily/include/spaceable-grob.hh b/lily/include/spaceable-grob.hh
index 5625bfd61e..98d4def1bc 100644
--- a/lily/include/spaceable-grob.hh
+++ b/lily/include/spaceable-grob.hh
@@ -11,12 +11,14 @@
 
 #include "lily-proto.hh"
 #include "grob-interface.hh"
+#include "spring.hh"
 
 struct Spaceable_grob
 {
   /// set a minimum distance
   static void add_rod (Grob *me, Grob *to, Real distance);
   static void add_spring (Grob *me, Grob *to, Real dist, Real strength);
+  static void add_spring (Grob *me, Grob *to, Spring sp);
   static void get_spring (Grob *me, Grob *other, Real *dist, Real *inv_strength);
 
   DECLARE_GROB_INTERFACE();
diff --git a/lily/include/spring.hh b/lily/include/spring.hh
index b49d3de1f4..d9b40cca46 100644
--- a/lily/include/spring.hh
+++ b/lily/include/spring.hh
@@ -1,5 +1,5 @@
 /*
-  spring.hh -- declare Spring, Column_spring
+  spring.hh -- declare Spring
 
   source file of the GNU LilyPond music typesetter
 
@@ -12,9 +12,8 @@
 #include "lily-proto.hh"
 #include "smobs.hh"
 
-struct Spring
+class Spring
 {
-  Grob *other_;
   Real distance_;
   Real min_distance_;
 
@@ -24,8 +23,24 @@ struct Spring
   DECLARE_SIMPLE_SMOBS (Spring);
 public:
   Spring ();
+  Spring (Real distance, Real min_distance);
+
+  Real distance () const {return distance_;}
+  Real min_distance () const {return min_distance_;}
+  Real inverse_stretch_strength () const {return inverse_stretch_strength_;}
+  Real inverse_compress_strength () const {return inverse_compress_strength_;}
+
+  void set_distance (Real);
+  void set_min_distance (Real);
+  void set_inverse_stretch_strength (Real);
+  void set_inverse_compress_strength (Real);
+
+  void operator*= (Real);
+  Grob *other_;
 };
 DECLARE_UNSMOB (Spring, spring);
 
+Spring merge_springs (vector<Spring> const &springs);
+
 #endif /* SPRING_HH */
 
diff --git a/lily/include/staff-spacing.hh b/lily/include/staff-spacing.hh
index 1553129b57..12472cc065 100644
--- a/lily/include/staff-spacing.hh
+++ b/lily/include/staff-spacing.hh
@@ -20,7 +20,7 @@ class Staff_spacing
 
 public:
   DECLARE_GROB_INTERFACE();
-  static Spring get_spacing_params (Grob *);
+  static Spring get_spacing (Grob *);
   static Interval bar_y_positions (Grob *);
 };
 
diff --git a/lily/note-spacing.cc b/lily/note-spacing.cc
index a886c31a41..1274fe98c4 100644
--- a/lily/note-spacing.cc
+++ b/lily/note-spacing.cc
@@ -27,9 +27,9 @@
   spacing?
 */
 
-void
+Spring
 Note_spacing::get_spacing (Grob *me, Item *right_col,
-			   Real base_space, Real increment, Real *space, Real *fixed)
+			   Real base_space, Real increment)
 {
   vector<Item*> note_columns = Spacing_interface::left_note_columns (me);
   Real left_head_end = 0;
@@ -65,19 +65,15 @@ Note_spacing::get_spacing (Grob *me, Item *right_col,
     the full amount of space.
   */
   Real min_dist = Spacing_interface::minimum_distance (me);
+  Real min_desired_space = max (left_head_end + (min_dist - left_head_end) / 2,
+				min_dist - (base_space - increment) / 2);
+  Real ideal = base_space - increment + min_desired_space;
 
-  *fixed = max (left_head_end + (min_dist - left_head_end) / 2,
-		min_dist - (base_space - increment) / 2);
-
-  /*
-    We don't do complicated stuff: (base_space - increment) is the
-    normal amount of white, which also determines the amount of
-    stretch. Upon (extreme) stretching, notes with accidentals should
-    stretch as much as notes without accidentals.
-  */
-  *space = (base_space - increment) + *fixed;
+  stem_dir_correction (me, right_col, increment, &ideal, &min_desired_space);
 
-  stem_dir_correction (me, right_col, increment, space, fixed);
+  Spring ret (ideal, min_dist);
+  ret.set_inverse_compress_strength (ideal - max (min_dist, min_desired_space));
+  return ret;
 }
 
 
diff --git a/lily/paper-column.cc b/lily/paper-column.cc
index 62f68b105e..e3f462894a 100644
--- a/lily/paper-column.cc
+++ b/lily/paper-column.cc
@@ -184,7 +184,7 @@ Paper_column::print (SCM p)
       vector<Offset> pts;
       pts.push_back (Offset (0, y));
 
-      Offset p2 (sp->distance_, y);
+      Offset p2 (sp->distance (), y);
       pts.push_back (p2);
       
       Stencil id_stencil = Lookup::points_to_line_stencil (0.1, pts);
@@ -192,9 +192,9 @@ Paper_column::print (SCM p)
 
       SCM distance_stc = Text_interface::interpret_markup (me->layout ()->self_scm (),
 							   small_letters,
-							   ly_string2scm (String_convert::form_string ("%5.2lf", sp->distance_)));
+							   ly_string2scm (String_convert::form_string ("%5.2lf", sp->distance ())));
       
-      id_stencil.add_stencil (unsmob_stencil (distance_stc)->translated (Offset (sp->distance_/3, y+1)));
+      id_stencil.add_stencil (unsmob_stencil (distance_stc)->translated (Offset (sp->distance ()/3, y+1)));
       id_stencil.add_stencil (head.translated (p2));
       id_stencil = id_stencil.in_color (0,0,1);
       l.add_stencil (id_stencil);
diff --git a/lily/spaceable-grob.cc b/lily/spaceable-grob.cc
index 77793e5492..464e0d3b1c 100644
--- a/lily/spaceable-grob.cc
+++ b/lily/spaceable-grob.cc
@@ -59,22 +59,6 @@ void
 Spaceable_grob::add_spring (Grob *me, Grob *other,
 			    Real distance, Real inverse_strength)
 {
-  if (distance < 0.0 || inverse_strength < 0.0)
-    {
-      programming_error ("adding reverse spring, setting to unit");
-      distance = 1.0;
-      inverse_strength = 1.0;
-    }
-
-  if (isinf (distance) || isnan (distance)
-      || isnan (inverse_strength))
-    {
-      /* strength == INF is possible. It means fixed distance.  */
-      programming_error ("insane distance found");
-      distance = 1.0;
-      inverse_strength = 1.0;
-    }
-
 #ifndef NDEBUG
   SCM mins = me->get_object ("ideal-distances");
   for (SCM s = mins; scm_is_pair (s); s = scm_cdr (s))
@@ -89,9 +73,9 @@ Spaceable_grob::add_spring (Grob *me, Grob *other,
 #endif
 
   Spring spring;
-  spring.inverse_stretch_strength_ = inverse_strength;
-  spring.inverse_compress_strength_ = inverse_strength;
-  spring.distance_ = distance;
+  spring.set_inverse_stretch_strength (inverse_strength);
+  spring.set_inverse_compress_strength (inverse_strength);
+  spring.set_distance (distance);
   spring.other_ = other;
 
   SCM ideal = me->get_object ("ideal-distances");
@@ -99,6 +83,15 @@ Spaceable_grob::add_spring (Grob *me, Grob *other,
   me->set_object ("ideal-distances", ideal);
 }
 
+void
+Spaceable_grob::add_spring (Grob *me, Grob *other, Spring sp)
+{
+  SCM ideal = me->get_object ("ideal-distances");
+  sp.other_ = other;
+  ideal = scm_cons (sp.smobbed_copy (), ideal);
+  me->set_object ("ideal-distances", ideal);
+}
+
 void
 Spaceable_grob::get_spring (Grob *this_col, Grob *next_col, Real *dist, Real *inv_strength)
 {
@@ -118,8 +111,8 @@ Spaceable_grob::get_spring (Grob *this_col, Grob *next_col, Real *dist, Real *in
     programming_error (_f ("No spring between column %d and next one",
 			   Paper_column::get_rank (this_col)));
 
-  *dist = (spring) ? spring->distance_ : 5.0;
-  *inv_strength = (spring) ? spring->inverse_stretch_strength_ : 1.0;
+  *dist = (spring) ? spring->distance () : 5.0;
+  *inv_strength = (spring) ? spring->inverse_stretch_strength () : 1.0;
 }
 
 
diff --git a/lily/spacing-determine-loose-columns.cc b/lily/spacing-determine-loose-columns.cc
index 7274ef1363..ef5e8c71de 100644
--- a/lily/spacing-determine-loose-columns.cc
+++ b/lily/spacing-determine-loose-columns.cc
@@ -154,23 +154,16 @@ Spacing_spanner::set_distances_for_loose_col (Grob *me, Grob *c,
 		The note spacing should be taken from the musical
 		columns.
 	      */
-	      Real space = 0.0;
-	      Real fixed = 0.0;
-		  
 	      Real base = note_spacing (me, lc, rc, options);
-	      Note_spacing::get_spacing (sp, rc, base, options->increment_,
-					 &space, &fixed);
+	      Spring spring = Note_spacing::get_spacing (sp, rc, base, options->increment_);
 
-	      space -= options->increment_;
-
-	      dists[d] = max (dists[d], space);
+	      dists[d] = max (dists[d], spring.distance () - options->increment_);
 	    }
 	  else if (Staff_spacing::has_interface (sp))
 	    {
-	      Spring spring = Staff_spacing::get_spacing_params (sp);
-	      Real fixed = spring.distance_ - spring.inverse_compress_strength_;
+	      Spring spring = Staff_spacing::get_spacing (sp);
 
-	      dists[d] = max (dists[d], fixed);
+	      dists[d] = max (dists[d], spring.min_distance ());
 	    }
 	  else
 	    programming_error ("Subversive spacing wish");
diff --git a/lily/spacing-interface.cc b/lily/spacing-interface.cc
index 82f6fccdb7..33a4e14d39 100644
--- a/lily/spacing-interface.cc
+++ b/lily/spacing-interface.cc
@@ -66,7 +66,7 @@ Spacing_interface::minimum_distance (Grob *me)
     }
   while (flip (&d) != LEFT);
 
-  return skylines[LEFT].distance (skylines[RIGHT]);
+  return max (0.0, skylines[LEFT].distance (skylines[RIGHT]));
 }
 
 /*
diff --git a/lily/spacing-spanner.cc b/lily/spacing-spanner.cc
index 69b0de980b..d158883dad 100644
--- a/lily/spacing-spanner.cc
+++ b/lily/spacing-spanner.cc
@@ -237,37 +237,15 @@ Spacing_spanner::musical_column_spacing (Grob *me,
 					 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_)
-    {
-      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
     {
-      int wish_count = 0;
-      
+      vector<Spring> springs;
       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];
@@ -281,32 +259,10 @@ Spacing_spanner::musical_column_spacing (Grob *me,
 	    This is probably a waste of time in the case of polyphonic
 	    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++;
-	    }
-	}
-
-      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;
+	    springs.push_back (Note_spacing::get_spacing (wish, right_col, base_note_space, options->increment_));
 	}
 
-      if (compound_note_space < 0 || wish_count == 0)
+      if (springs.empty ())
 	{
 
 	  if (!Paper_column::is_musical (right_col))
@@ -317,9 +273,8 @@ Spacing_spanner::musical_column_spacing (Grob *me,
 		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
 	    {
@@ -327,44 +282,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.
 	    
-		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
 	    
-		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
-	{
-	  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.
   */
@@ -379,16 +320,11 @@ 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.
       */
-      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);
 }
 
 /*
@@ -460,9 +396,9 @@ Spacing_spanner::breakable_column_spacing (Grob *me, Item *l, Item *r,
 	  */
 	  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_;
+	  Spring sp = Staff_spacing::get_spacing (spacing_grob);
+	  Real space = sp.distance ();
+	  Real fixed = sp.min_distance ();
 
 	  if (Paper_column::when_mom (r).grace_part_)
 	    {
diff --git a/lily/spring-smob.cc b/lily/spring-smob.cc
index a466fb44bb..5639a1cd07 100644
--- a/lily/spring-smob.cc
+++ b/lily/spring-smob.cc
@@ -10,15 +10,6 @@
 #include "warn.hh"
 #include "ly-smobs.icc"
 
-Spring::Spring ()
-{
-  distance_ = 1.0;
-  min_distance_ = 1.0;
-  inverse_stretch_strength_ = 1.0;
-  inverse_compress_strength_ = 1.0;
-  other_ = 0;
-}
-
 IMPLEMENT_SIMPLE_SMOBS (Spring);
 
 SCM
diff --git a/lily/staff-spacing.cc b/lily/staff-spacing.cc
index 08ef578399..b9fd874b9e 100644
--- a/lily/staff-spacing.cc
+++ b/lily/staff-spacing.cc
@@ -107,7 +107,7 @@ Staff_spacing::next_notes_correction (Grob *me,
    will be the distance between columns if there is a compression force of 1.0
    applied to the line. */
 Spring
-Staff_spacing::get_spacing_params (Grob *me)
+Staff_spacing::get_spacing (Grob *me)
 {
   Grob *separation_item = 0;
   Item *me_item = dynamic_cast<Item *> (me);
@@ -199,10 +199,8 @@ Staff_spacing::get_spacing_params (Grob *me)
   fixed += correction;
   ideal += correction;
 
-  Spring ret;
-  ret.min_distance_ = max (min_dist, fixed);
-  ret.distance_ = ideal;
-  ret.inverse_stretch_strength_ = ret.inverse_compress_strength_ = ideal - fixed;
+  Spring ret (ideal, min_dist);
+  ret.set_inverse_stretch_strength (ideal - fixed);
   return ret;
 }
 
-- 
2.39.5