]> git.donarmstrong.com Git - lilypond.git/commitdiff
one-pass vertical stretching
authorJoe Neeman <joeneeman@gmail.com>
Thu, 18 Jan 2007 13:01:45 +0000 (15:01 +0200)
committerJoe Neeman <joeneeman@gmail.com>
Thu, 18 Jan 2007 13:01:45 +0000 (15:01 +0200)
13 files changed:
lily/align-interface.cc
lily/axis-group-interface.cc
lily/include/align-interface.hh
lily/include/axis-group-interface.hh
lily/include/system.hh
lily/page-breaking.cc
lily/paper-score.cc
lily/prob-scheme.cc
lily/system.cc
scm/define-grob-properties.scm
scm/define-grobs.scm
scm/layout-page-dump.scm
scm/layout-page-layout.scm

index fc62e45eb3eba2b28ee5dbbfbb475897b5d40309..e52e8f69ff7c1891609d153ce0e2025097a5469d 100644 (file)
@@ -307,6 +307,27 @@ Align_interface::align_elements_to_extents (Grob *me, Axis a)
       all_grobs[j]->translate_axis (translates[j], a);
 }
 
+/* After we have already determined the y-offsets of our children, we may still
+   want to stretch them a little. */
+void
+Align_interface::stretch (Grob *me, Real amount, Axis a)
+{
+  extract_grob_set (me, "elements", elts);
+  Real non_empty_elts = 0.0;
+  for (vsize i = 0; i < elts.size (); i++)
+    non_empty_elts += !elts[i]->extent (me, a).is_empty ();
+
+  Real offset = 0.0;
+  Direction dir = robust_scm2dir (me->get_property ("stacking-dir"), DOWN);
+  for (vsize i = 0; i < elts.size (); i++)
+    {
+      elts[i]->translate_axis (dir * offset, a);
+      if (!elts[i]->extent (me, a).is_empty ())
+       offset += amount / non_empty_elts;
+    }
+  me->flush_extent_cache (Y_AXIS);
+}
+
 Real
 Align_interface::get_pure_child_y_translation (Grob *me, Grob *ch, int start, int end)
 {
@@ -376,6 +397,31 @@ Align_interface::set_ordered (Grob *me)
   ga->set_ordered (true);
 }
 
+MAKE_SCHEME_CALLBACK (Align_interface, calc_max_stretch, 1)
+SCM
+Align_interface::calc_max_stretch (SCM smob)
+{
+  Grob *me = unsmob_grob (smob);
+  Spanner *spanner_me = dynamic_cast<Spanner*> (me);
+  Real ret = 0;
+
+  if (spanner_me)
+    {
+      Paper_column *left = dynamic_cast<Paper_column*> (spanner_me->get_bound (LEFT));
+      Real height = me->extent (me, Y_AXIS).length ();
+      SCM line_break_details = left->get_property ("line-break-system-details");
+      SCM fixed_offsets = scm_assq (ly_symbol2scm ("alignment-offsets"),
+                                   line_break_details);
+
+      /* if there are fixed offsets, we refuse to stretch */
+      if (fixed_offsets != SCM_BOOL_F)
+       ret = 0;
+      else
+       ret = height * height / 80.0; /* why this, exactly? -- jneem */
+    }
+  return scm_from_double (ret);
+}
+
 /*
   Find Y-axis parent of G that has a #'forced-distance property. This
   has the effect of finding the piano-staff given an object in that
index 23c30872474d31267c92d093122dd1be452d6a1b..e87a108e7a200e9a7bb8247432290bab164b6f21 100644 (file)
@@ -486,6 +486,21 @@ Axis_group_interface::skyline_spacing (Grob *me, vector<Grob*> elements)
   return skylines;
 }
 
+MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_max_stretch, 1)
+SCM
+Axis_group_interface::calc_max_stretch (SCM smob)
+{
+  Grob *me = unsmob_grob (smob);
+  Real ret = 0;
+  extract_grob_set (me, "elements", elts);
+
+  for (vsize i = 0; i < elts.size (); i++)
+    if (Axis_group_interface::has_interface (elts[i]))
+      ret += robust_scm2double (elts[i]->get_property ("max-stretch"), 0.0);
+
+  return scm_from_double (ret);
+}
+
 ADD_INTERFACE (Axis_group_interface,
 
               "An object that groups other layout objects.",
@@ -495,6 +510,7 @@ ADD_INTERFACE (Axis_group_interface,
               "Y-common "
               "axes "
               "elements "
+              "max-stretch "
               "pure-Y-common "
               "pure-relevant-elements "
               "skylines "
index 4f1d3491419a228a2604d6bf789345d82d8738d8..0d0f0958468b5a69eca0cc96d9b57ef173d42b32 100644 (file)
@@ -17,6 +17,8 @@ struct Align_interface
 {
   DECLARE_SCHEME_CALLBACK (calc_positioning_done, (SCM));
   DECLARE_SCHEME_CALLBACK (stretch_after_break, (SCM element));
+  DECLARE_SCHEME_CALLBACK (calc_max_stretch, (SCM));
+  static void stretch (Grob *, Real amount, Axis a);
   static void align_to_fixed_distance (Grob *, Axis a);
   static void align_elements_to_extents (Grob *, Axis a);
   static vector<Real> get_extents_aligned_translates (Grob *, vector<Grob*> const&,
index d752a05bb46189557679157e372dbae27c51b492..2f4f501cee8db919a9f7992f9d030c51f45a1f4e 100644 (file)
@@ -25,6 +25,7 @@ struct Axis_group_interface
   DECLARE_SCHEME_CALLBACK (pure_height, (SCM smob, SCM start, SCM end));
   DECLARE_SCHEME_CALLBACK (calc_skylines, (SCM smob));
   DECLARE_SCHEME_CALLBACK (combine_skylines, (SCM smob));
+  DECLARE_SCHEME_CALLBACK (calc_max_stretch, (SCM smob));
   static Interval relative_group_extent (vector<Grob*> const &list,
                                         Grob *common, Axis);
   static Interval relative_pure_height (Grob *me, vector<Grob*> const &list,
index 48e6875dfb43e4aaa2d1984602a5658a7f928dfc..3a5b39ad4e75104cec0d141affe71dd26c78486b 100644 (file)
@@ -20,7 +20,7 @@ class System : public Spanner
 {
   int rank_;
   Grob_array *all_elements_;
-  Drul_array<Skyline> skylines_;
+  Skyline_pair skylines_;
   void build_skylines ();
   void init_elements ();
   friend class Paper_score;    // ugh.
@@ -29,9 +29,11 @@ class System : public Spanner
 public:
   Paper_score *paper_score () const;
   int get_rank () const;
+  void do_break_substitution_and_fixup_refpoints ();
   void post_processing ();
   SCM get_paper_system ();
   SCM get_paper_systems ();
+  SCM get_broken_system_grobs ();
 
   System (SCM);
   System (System const &);
index e531b7f4addd8de689e58e797c66e6d02a0d59f1..d82b881a21c554427c0e05453e6aad24d69608df 100644 (file)
@@ -100,8 +100,9 @@ Page_breaking::systems ()
     {
       if (all_[sys].pscore_)
        {
-         SCM lines = all_[sys].pscore_->root_system ()->get_paper_systems ();
-         ret = scm_cons (scm_vector_to_list (lines), ret);
+         all_[sys].pscore_->root_system ()->do_break_substitution_and_fixup_refpoints ();
+         SCM lines = all_[sys].pscore_->root_system ()->get_broken_system_grobs ();
+         ret = scm_cons (lines, ret);
        }
       else
        {
@@ -178,7 +179,7 @@ Page_breaking::make_pages (vector<vsize> lines_per_page, SCM systems)
   SCM layout_module = scm_c_resolve_module ("scm layout-page-layout");
   SCM page_module = scm_c_resolve_module ("scm page");
 
-  SCM make_page = scm_c_module_lookup (layout_module, "make-page-from-systems");
+  SCM make_page = scm_c_module_lookup (layout_module, "stretch-and-draw-page");
   SCM page_stencil = scm_c_module_lookup (page_module, "page-stencil");
   make_page = scm_variable_ref (make_page);
   page_stencil = scm_variable_ref (page_stencil);
index bd6d3a6ede9976aeec333e0a5bca38c51f3e41f7..0142ef3b926698e68cc90b4a9078935d8eb12910 100644 (file)
@@ -152,6 +152,7 @@ Paper_score::get_paper_systems ()
       vector<Column_x_positions> breaking = calc_breaking ();
       system_->break_into_pieces (breaking);
       message (_ ("Drawing systems...") + " ");
+      system_->do_break_substitution_and_fixup_refpoints ();
       paper_systems_ = system_->get_paper_systems ();
     }
   return paper_systems_;
index f1e917966176d69698d71d436244c4d3c0a46f5f..dc53e51d3387e1bdae82b12774a3a6c08be4fcbe 100644 (file)
@@ -7,6 +7,7 @@
 */
 
 #include "prob.hh"
+#include "skyline.hh"
 
 LY_DEFINE (ly_prob_set_property_x, "ly:prob-set-property!",
           2, 1, 0, (SCM obj, SCM sym, SCM value),
@@ -83,3 +84,28 @@ LY_DEFINE(ly_paper_system_p, "ly:paper-system?",
 {
   return ly_prob_type_p (obj, ly_symbol2scm ("paper-system"));
 }
+
+LY_DEFINE (ly_paper_system_minimum_distance, "ly:paper-system-minimum-distance",
+          2, 0, 0, (SCM sys1, SCM sys2),
+          "Measure the minimum distance between these two paper-systems "
+          "using their stored skylines if possible and falling back to "
+          "their extents otherwise.")
+{
+  Real ret = 0;
+  Prob *p1 = unsmob_prob (sys1);
+  Prob *p2 = unsmob_prob (sys2);
+  Skyline_pair *sky1 = Skyline_pair::unsmob (p1->get_property ("skylines"));
+  Skyline_pair *sky2 = Skyline_pair::unsmob (p2->get_property ("skylines"));
+
+  if (sky1 && sky2)
+    ret = (*sky1)[DOWN].distance ((*sky2)[UP]);
+  else
+    {
+      Stencil *s1 = unsmob_stencil (p1->get_property ("stencil"));
+      Stencil *s2 = unsmob_stencil (p2->get_property ("stencil"));
+      Interval iv1 = s1->extent (Y_AXIS);
+      Interval iv2 = s2->extent (Y_AXIS);
+      ret = iv2[UP] - iv1[DOWN];
+    }
+  return scm_from_double (ret);
+}
index 06ac09e39a224b6b31467474d88e14e54bf1d341..d71fa20b48be8a5ff3503c22be895adf264ab5f5 100644 (file)
@@ -114,8 +114,8 @@ fixup_refpoints (vector<Grob*> const &grobs)
     grobs[i]->fixup_refpoint ();
 }
 
-SCM
-System::get_paper_systems ()
+void
+System::do_break_substitution_and_fixup_refpoints ()
 {
   for (vsize i = 0; i < all_elements_->size (); i++)
     {
@@ -178,7 +178,20 @@ System::get_paper_systems ()
 
   if (be_verbose_global)
     message (_f ("Element count %d.", count + element_count ()));
+}
+
+SCM
+System::get_broken_system_grobs ()
+{
+  SCM ret = SCM_EOL;
+  for (vsize i = 0; i < broken_intos_.size (); i++)
+    ret = scm_cons (broken_intos_[i]->self_scm (), ret);
+  return scm_reverse (ret);
+}
 
+SCM
+System::get_paper_systems ()
+{
   SCM lines = scm_c_make_vector (broken_intos_.size (), SCM_EOL);
   for (vsize i = 0; i < broken_intos_.size (); i++)
     {
@@ -187,14 +200,6 @@ System::get_paper_systems ()
 
       System *system = dynamic_cast<System *> (broken_intos_[i]);
 
-      system->post_processing ();
-      system->build_skylines ();
-      if (i > 0)
-       {
-         System *prev = dynamic_cast<System*> (broken_intos_[i-1]);
-         Real r = prev->skylines_[DOWN].distance (system->skylines_[UP]);
-         system->set_property ("skyline-distance", scm_from_double (r));
-       }
       scm_vector_set_x (lines, scm_from_int (i),
                        system->get_paper_system ());
 
@@ -334,6 +339,9 @@ System::get_paper_system ()
   SCM exprs = SCM_EOL;
   SCM *tail = &exprs;
 
+  post_processing ();
+  build_skylines ();
+
   vector<Layer_entry> entries;
   for (vsize j = 0; j < all_elements_->size (); j++)
     {
@@ -391,25 +399,21 @@ System::get_paper_system ()
 
   /* information that the page breaker might need */
   Grob *right_bound = this->get_bound (RIGHT);
-  pl->set_property ("skyline-distance", get_property ("skyline-distance"));
+  pl->set_property ("skylines", skylines_.smobbed_copy ());
   pl->set_property ("page-break-permission", right_bound->get_property ("page-break-permission"));
   pl->set_property ("page-turn-permission", right_bound->get_property ("page-turn-permission"));
   pl->set_property ("page-break-penalty", right_bound->get_property ("page-break-penalty"));
   pl->set_property ("page-turn-penalty", right_bound->get_property ("page-turn-penalty"));
 
-  if (!scm_is_pair (pl->get_property ("refpoint-Y-extent")))
-    {
-      Interval staff_refpoints;
-      staff_refpoints.set_empty ();
-      extract_grob_set (this, "spaceable-staves", staves);
-      for (vsize i = 0; i < staves.size (); i++)
-       {
-         Grob *g = staves[i];
-         staff_refpoints.add_point (g->relative_coordinate (this, Y_AXIS));
-       }
-      pl->set_property ("refpoint-Y-extent", ly_interval2scm (staff_refpoints));
-    }
+  /* remove me if make web succeeds */
+  assert (!scm_is_pair (pl->get_property ("refpoint-Y-extent")));
+
+  Interval staff_refpoints;
+  extract_grob_set (this, "spaceable-staves", staves);
+  for (vsize i = 0; i < staves.size (); i++)
+    staff_refpoints.add_point (staves[i]->relative_coordinate (this, Y_AXIS));
 
+  pl->set_property ("staff-refpoint-extent", ly_interval2scm (staff_refpoints));
   pl->set_property ("system-grob", this->self_scm ()); 
 
   return pl->unprotect ();
@@ -536,4 +540,5 @@ ADD_INTERFACE (System,
               "spaceable-staves "
               "skyline-distance "
               "skyline-horizontal-padding "
+              "staff-refpoint-extent "
               )
index c7949ece77c9c6aa8f996d725accef52939b536c..bddef11f2c8b4da0322a49354527e20c774d440b 100644 (file)
@@ -241,6 +241,8 @@ column is the start of a system.")
      (long-text ,markup? "Text markup.  See @usermanref{Text markup}.")
      (max-beam-connect ,integer? "Maximum number of beams to connect
 to beams from this stem. Further beams are typeset as beamlets.")
+     (max-stretch ,number? "The maximum amount that this vertical-axis-group
+can be vertically stretched (for example, in order to better fill a page).")
      (measure-length ,ly:moment? "Length of a
 measure. Used in some spacing situations.")
      (measure-count ,integer? "The number of measures for a
index 2130dab4be14fe07b8a65170eec4be5f7316ac56..9cb27bdf547ac4c8b30f92506806a2aa32732434 100644 (file)
        (X-extent . ,ly:axis-group-interface::width)
        (Y-extent . ,ly:axis-group-interface::height)
        (skylines . ,ly:axis-group-interface::calc-skylines)
+       (max-stretch . ,ly:axis-group-interface::calc-max-stretch)
        (skyline-horizontal-padding . 1.0)
        (meta . ((class . System)
                 (interfaces . (system-interface
        (stacking-dir . -1)
        (padding . 0.5)
        (skylines . ,ly:axis-group-interface::combine-skylines)
+       (max-stretch . ,ly:align-interface::calc-max-stretch)
        (meta . ((class . Spanner)
                 (object-callbacks . ((Y-common . ,ly:axis-group-interface::calc-y-common)))
                 (interfaces . (align-interface
        (Y-offset . ,ly:hara-kiri-group-spanner::force-hara-kiri-callback)
        (Y-extent . ,ly:hara-kiri-group-spanner::y-extent)
        (X-extent . ,ly:axis-group-interface::width)
-       (skylines . ,ly:axis-group-interface::calc-skylines);
+       (skylines . ,ly:axis-group-interface::calc-skylines)
+       (max-stretch . ,ly:axis-group-interface::calc-max-stretch)
        (meta . ((class . Spanner)
                 (object-callbacks . ((X-common . ,ly:axis-group-interface::calc-x-common)))
                 (interfaces . (axis-group-interface
index 8237106f063ff967f447d05ae27cde2dd41a307b..acb8cb177ac760e5947a66f228ddce9431f34610 100644 (file)
            ;; utilisties for writing other page dump functions
            record-tweaks dump-all-tweaks))
 
+(define (stretchable-line? line)
+  "Say whether a system can be stretched."
+  (not (or (ly:prob-property? line 'is-title)
+          (let ((system-extent (paper-system-staff-extents line)))
+            (= (interval-start system-extent)
+               (interval-end   system-extent))))))
+
 (define (record-tweaks what property-pairs tweaks)
   (let ((key (ly:output-def-lookup (ly:grob-layout what)
                                   'tweak-key
index 264d38054871e015b83f2f0f6e5f09e99d24c1de..c5721b325a98711b2d89c4ff7195705ea89e50fd 100644 (file)
            line-minimum-distance line-ideal-distance
            first-line-position
            line-ideal-relative-position line-minimum-relative-position
-            line-minimum-position-on-page stretchable-line?
+            line-minimum-position-on-page
            page-maximum-space-to-fill page-maximum-space-left space-systems))
 
+(define (stretch-and-draw-page paper-book systems page-number ragged last)
+  (define (stretchable? sys)
+    (and (ly:grob? sys)
+        (ly:grob-property sys 'stretchable)))
+
+  (define (height-estimate sys)
+    (interval-length
+     (if (ly:grob? sys)
+        (ly:grob-property sys 'pure-Y-extent)
+        (paper-system-extent sys Y))))
+
+  (define (max-stretch sys)
+    (if (stretchable? sys)
+       (ly:grob-property sys 'max-stretch)
+       0.0))
+
+  (define (print-system sys)
+    (if (ly:grob? sys)
+       (ly:system-print sys)
+       sys))
+
+  (define (set-line-stretch! sorted-lines rest-height space-left)
+    (if (not (null? sorted-lines))
+       (let* ((line (first sorted-lines))
+              (height (height-estimate line))
+              (stretch (min (max-stretch line)
+                            (if (positive? rest-height)
+                                (/ (* height space-left) rest-height)
+                                  0.0))))
+         (if (stretchable? line)
+             (ly:system-stretch line stretch))
+         (set-line-stretch! (cdr sorted-lines)
+                            (if (stretchable? line)
+                                (- rest-height height)
+                                rest-height)
+                            (- space-left stretch)))))
+
+  (let* ((page (make-page paper-book
+                         'page-number page-number
+                         'is-last last))
+        (paper (ly:paper-book-paper paper-book))
+        (height (page-printable-height page))
+        ; there is a certain amount of impreciseness going on here:
+        ; the system heights are estimated, we aren't using skyline distances
+        ; yet, etc. If we overstretch because of underestimation, the result
+        ; is very bad. So we stick in some extra space, just to be sure.
+        (buffer (/ height 10.0))
+        (total-system-height (apply + (map height-estimate systems)))
+        (height-left (- height total-system-height buffer)))
+
+    (if (not ragged)
+       (set-line-stretch! (sort systems
+                                (lambda (s1 s2)
+                                  (< (height-estimate s1)
+                                     (height-estimate s2))))
+                          (apply + (map height-estimate
+                                        (filter stretchable? systems)))
+                          (- (page-printable-height page)
+                             total-system-height)))
+
+    (let* ((lines (map print-system systems))
+          (posns (if (null? lines)
+                     (list)
+                     (let* ((paper (ly:paper-book-paper paper-book))
+                            (space-to-fill (page-maximum-space-to-fill
+                                            page lines paper))
+                            (spacing (space-systems space-to-fill lines ragged paper #f)))
+                       (if (and (> (length lines) 1)
+                                (or (not (car spacing)) (inf? (car spacing))))
+                           (begin
+                             (ly:warning (_ "Can't fit systems on page -- ignoring between-system-padding"))
+                             (cdr (space-systems space-to-fill lines ragged paper #t)))
+                           (cdr spacing))))))
+      (page-set-property! page 'lines lines)
+      (page-set-property! page 'configuration posns)
+      page)))
+
 (define (page-breaking-wrapper paper-book)
   "Compute line and page breaks by calling the page-breaking paper variable,
   then performs the post process function using the page-post-process paper
 ;;;
 ;;; Utilities for computing line distances and positions
 ;;;
+(define (line-extent line)
+  "Return the extent of the line (its lowest and highest Y-coordinates)."
+  (paper-system-extent line Y))
+
 (define (line-height line)
   "Return the system height, that is the length of its vertical extent."
-  (interval-length (paper-system-extent line Y)))
+  (interval-length (line-extent line)))
 
 (define (line-next-space line next-line layout)
   "Return space to use between `line' and `next-line'.
   "Minimum distance between `line' reference position and `next-line'
  reference position. If next-line is #f, return #f."
   (and next-line
-       (let ((non-skyline-distance (- (interval-end (paper-system-extent next-line Y))
-                                     (interval-start (paper-system-extent line Y)))))
-        (max 0 (+ (ly:prob-property next-line 'skyline-distance non-skyline-distance)
-                  (if ignore-padding 0 (line-next-padding line next-line layout)))))))
+       (let ((padding (if ignore-padding
+                         0
+                         (line-next-padding line next-line layout))))
+        (if (or (ly:grob? line) (ly:grob? next-line))
+            (max 0 (+ padding
+                      (- (interval-start (line-extent line))
+                         (interval-end (line-extent next-line)))))
+            (max 0 (+ padding
+                      (ly:paper-system-minimum-distance line next-line)))))))
 
 (define (line-ideal-distance line next-line layout ignore-padding)
   "Ideal distance between `line' reference position and `next-line'
              0.0
            (ly:output-def-lookup layout 'page-top-space))
          (interval-end (paper-system-staff-extents line)))
-       (interval-end (paper-system-extent line Y))))
+       (interval-end (line-extent line))))
 
 (define (line-ideal-relative-position line prev-line layout ignore-padding)
   "Return ideal position of `line', relative to `prev-line' position.
          (position (+ (line-minimum-relative-position line prev-line layout #f)
                       (if prev-line prev-position 0.0)))
          (bottom-position (- position
-                             (interval-start (paper-system-extent line Y)))))
+                             (interval-start (line-extent line)))))
     (and (or (not prev-line)
              (< bottom-position (page-printable-height page)))
          position)))
 
-(define (stretchable-line? line)
-  "Say whether a system can be stretched."
-  (not (or (ly:prob-property? line 'is-title)
-          (let ((system-extent (paper-system-staff-extents line)))
-            (= (interval-start system-extent)
-               (interval-end   system-extent))))))
-
 (define (page-maximum-space-to-fill page lines paper)
   "Return the space between the first line top position and the last line
   bottom position. This constitutes the maximum space to fill on `page'
        (first-line-position (first lines) paper)
        (ly:prob-property last-line
                         'bottom-space 0.0)
-       (- (interval-start (paper-system-extent last-line Y))))))
+       (- (interval-start (line-extent last-line))))))
 
 (define (page-maximum-space-left page)
   (let ((paper (ly:paper-book-paper (page-property page 'paper-book))))
                 (and position
                      (- (page-printable-height page)
                         (- position
-                           (interval-start (paper-system-extent line Y)))))
+                           (interval-start (line-extent line)))))
                 (bottom-position (cdr lines) line position)))))))
 
 ;;;
                   (+ y topskip)))
               (cdr space-result)))))
 
-(define (make-page-from-systems paper-book lines page-number ragged last)
-  "Return a new page, filled with `lines'."
-  (let* ((page (make-page paper-book
-                         'lines lines
-                         'page-number page-number
-                         'is-last last))
-        (posns (if (null? lines)
-                   (list)
-                   (let* ((paper (ly:paper-book-paper paper-book))
-                          (space-to-fill (page-maximum-space-to-fill
-                                           page lines paper))
-                          (spacing (space-systems space-to-fill lines ragged paper #f)))
-                     (if (and (> (length lines) 1)
-                              (or (not (car spacing)) (inf? (car spacing))))
-                         (begin
-                           (ly:warning (_ "Can't fit systems on page -- ignoring between-system-padding"))
-                           (cdr (space-systems space-to-fill lines ragged paper #t)))
-                         (cdr spacing))))))
-    (page-set-property! page 'configuration posns)
-    page))
 
 ;;;
 ;;; Page breaking function