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)
{
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
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.",
"Y-common "
"axes "
"elements "
+ "max-stretch "
"pure-Y-common "
"pure-relevant-elements "
"skylines "
{
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&,
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,
{
int rank_;
Grob_array *all_elements_;
- Drul_array<Skyline> skylines_;
+ Skyline_pair skylines_;
void build_skylines ();
void init_elements ();
friend class Paper_score; // ugh.
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 &);
{
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
{
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);
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_;
*/
#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),
{
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);
+}
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++)
{
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++)
{
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 ());
SCM exprs = SCM_EOL;
SCM *tail = &exprs;
+ post_processing ();
+ build_skylines ();
+
vector<Layer_entry> entries;
for (vsize j = 0; j < all_elements_->size (); j++)
{
/* 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 ();
"spaceable-staves "
"skyline-distance "
"skyline-horizontal-padding "
+ "staff-refpoint-extent "
)
(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
(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
;; 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
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