From: Joe Neeman Date: Thu, 18 Jan 2007 13:01:45 +0000 (+0200) Subject: one-pass vertical stretching X-Git-Tag: release/2.11.15-1~9^2~17 X-Git-Url: https://git.donarmstrong.com/?a=commitdiff_plain;h=1108d3c42d9b47e0953528e94254d0fdfd0f531a;p=lilypond.git one-pass vertical stretching --- diff --git a/lily/align-interface.cc b/lily/align-interface.cc index fc62e45eb3..e52e8f69ff 100644 --- a/lily/align-interface.cc +++ b/lily/align-interface.cc @@ -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 (me); + Real ret = 0; + + if (spanner_me) + { + Paper_column *left = dynamic_cast (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 diff --git a/lily/axis-group-interface.cc b/lily/axis-group-interface.cc index 23c3087247..e87a108e7a 100644 --- a/lily/axis-group-interface.cc +++ b/lily/axis-group-interface.cc @@ -486,6 +486,21 @@ Axis_group_interface::skyline_spacing (Grob *me, vector 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 " diff --git a/lily/include/align-interface.hh b/lily/include/align-interface.hh index 4f1d349141..0d0f095846 100644 --- a/lily/include/align-interface.hh +++ b/lily/include/align-interface.hh @@ -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 get_extents_aligned_translates (Grob *, vector const&, diff --git a/lily/include/axis-group-interface.hh b/lily/include/axis-group-interface.hh index d752a05bb4..2f4f501cee 100644 --- a/lily/include/axis-group-interface.hh +++ b/lily/include/axis-group-interface.hh @@ -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 const &list, Grob *common, Axis); static Interval relative_pure_height (Grob *me, vector const &list, diff --git a/lily/include/system.hh b/lily/include/system.hh index 48e6875dfb..3a5b39ad4e 100644 --- a/lily/include/system.hh +++ b/lily/include/system.hh @@ -20,7 +20,7 @@ class System : public Spanner { int rank_; Grob_array *all_elements_; - Drul_array 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 &); diff --git a/lily/page-breaking.cc b/lily/page-breaking.cc index e531b7f4ad..d82b881a21 100644 --- a/lily/page-breaking.cc +++ b/lily/page-breaking.cc @@ -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 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); diff --git a/lily/paper-score.cc b/lily/paper-score.cc index bd6d3a6ede..0142ef3b92 100644 --- a/lily/paper-score.cc +++ b/lily/paper-score.cc @@ -152,6 +152,7 @@ Paper_score::get_paper_systems () vector 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_; diff --git a/lily/prob-scheme.cc b/lily/prob-scheme.cc index f1e9179661..dc53e51d33 100644 --- a/lily/prob-scheme.cc +++ b/lily/prob-scheme.cc @@ -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); +} diff --git a/lily/system.cc b/lily/system.cc index 06ac09e39a..d71fa20b48 100644 --- a/lily/system.cc +++ b/lily/system.cc @@ -114,8 +114,8 @@ fixup_refpoints (vector 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 (broken_intos_[i]); - system->post_processing (); - system->build_skylines (); - if (i > 0) - { - System *prev = dynamic_cast (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 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 " ) diff --git a/scm/define-grob-properties.scm b/scm/define-grob-properties.scm index c7949ece77..bddef11f2c 100644 --- a/scm/define-grob-properties.scm +++ b/scm/define-grob-properties.scm @@ -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 diff --git a/scm/define-grobs.scm b/scm/define-grobs.scm index 2130dab4be..9cb27bdf54 100644 --- a/scm/define-grobs.scm +++ b/scm/define-grobs.scm @@ -1681,6 +1681,7 @@ (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 @@ -2007,6 +2008,7 @@ (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 @@ -2017,7 +2019,8 @@ (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 diff --git a/scm/layout-page-dump.scm b/scm/layout-page-dump.scm index 8237106f06..acb8cb177a 100644 --- a/scm/layout-page-dump.scm +++ b/scm/layout-page-dump.scm @@ -16,6 +16,13 @@ ;; 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 diff --git a/scm/layout-page-layout.scm b/scm/layout-page-layout.scm index 264d380548..c5721b325a 100644 --- a/scm/layout-page-layout.scm +++ b/scm/layout-page-layout.scm @@ -20,9 +20,86 @@ 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 @@ -47,9 +124,13 @@ ;;; ;;; 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'. @@ -79,10 +160,15 @@ "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' @@ -100,7 +186,7 @@ 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. @@ -129,18 +215,11 @@ (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' @@ -150,7 +229,7 @@ (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)))) @@ -166,7 +245,7 @@ (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))))))) ;;; @@ -202,26 +281,6 @@ (+ 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