@menu
* Vertical spacing inside a system::
-* Vertical spacing of piano staves::
* Vertical spacing between systems::
* Controlling spacing of individual systems::
* Two-pass vertical spacing::
@inputfileref{input/regression/,alignment-vertical-spacing.ly}.
-@node Vertical spacing of piano staves
-@subsection Vertical spacing of piano staves
-
-The distance between staves of a @internalsref{PianoStaff} cannot be
-computed during formatting. Rather, to make cross-staff beaming work
-correctly, that distance has to be fixed beforehand.
-
-The distance of staves in a @code{PianoStaff} is set with the
-@code{forced-distance} property of the
-@internalsref{VerticalAlignment} object, created in
-@internalsref{PianoStaff}.
-
-It can be adjusted as follows
-@example
-\new PianoStaff \with @{
- \override VerticalAlignment #'forced-distance = #7
-@} @{
- ...
-@}
-@end example
-
-@noindent
-This would bring the staves together at a distance of 7 staff spaces,
-measured from the center line of each staff.
-
-The difference is demonstrated in the following example,
-@lilypond[quote,verbatim]
-\relative c'' <<
- \new PianoStaff \with {
- \override VerticalAlignment #'forced-distance = #7
- } <<
- \new Staff { c1 }
- \new Staff { c }
- >>
- \new PianoStaff <<
- \new Staff { c }
- \new Staff { c }
- >>
->>
-@end lilypond
-
-
-@seealso
-
-Example files: @inputfileref{input/regression/,alignment-vertical-spacing.ly}.
-
-
@node Vertical spacing between systems
@subsection Vertical spacing between systems
\context {
\PianoStaff
\accepts Dynamics
- \override VerticalAlignment #'forced-distance = #7
}
}
}
>>
\layout {
- \context {
- \PianoStaff
- \override VerticalAlignment #'forced-distance = #10
- }
line-width = 18.0 \cm
}
\override Stem #'stemlet-length = #0.5
\override Slur #'height-limit = #1.5
}
- \context {
- \PianoStaff
- \override VerticalAlignment #'forced-distance = #10
- }
}
\midi {
SCM axis = scm_car (me->get_property ("axes"));
Axis ax = Axis (scm_to_int (axis));
- SCM force = me->get_property ("forced-distance");
- if (scm_is_number (force))
- Align_interface::align_to_fixed_distance (me, ax);
- else
- Align_interface::align_elements_to_extents (me, ax);
+ Align_interface::align_elements_to_extents (me, ax);
return SCM_BOOL_T;
}
/*
- merge with align-to-extents?
+ TODO: This belongs to the old two-pass spacing. Delete me.
*/
MAKE_SCHEME_CALLBACK(Align_interface, stretch_after_break, 1)
SCM
return SCM_UNSPECIFIED;
}
-/*
- merge with align-to-extents?
-*/
-void
-Align_interface::align_to_fixed_distance (Grob *me, Axis a)
-{
- Direction stacking_dir = robust_scm2dir (me->get_property ("stacking-dir"),
- DOWN);
-
- Real dy = robust_scm2double (me->get_property ("forced-distance"), 0.0);
-
- extract_grob_set (me, "elements", elem_source);
-
- vector<Grob*> elems (elem_source); // writable..
-
- Real where = 0;
-
- Interval v;
- v.set_empty ();
- vector<Real> translates;
-
- for (vsize j = elems.size (); j--;)
- {
- /*
- This is not very elegant, in that we need special support for
- hara-kiri. Unfortunately, the generic wiring of
- force_hara_kiri_callback () (extent and offset callback) is
- such that we might get into a loop if we call extent () or
- offset () the elements.
- */
- if (a == Y_AXIS
- && Hara_kiri_group_spanner::has_interface (elems[j]))
- Hara_kiri_group_spanner::consider_suicide (elems[j]);
-
- if (!elems[j]->is_live ())
- elems.erase (elems.begin () + j);
- }
-
- for (vsize j = 0; j < elems.size (); j++)
- {
- where += stacking_dir * dy;
- translates.push_back (where);
- v.unite (Interval (where, where));
- }
-
- for (vsize i = 0; i < translates.size (); i++)
- elems[i]->translate_axis (translates[i] - v.center (), a);
-}
-
/* for each grob, find its upper and lower skylines. If the grob has
an empty extent, delete it from the list instead. If the extent is
non-empty but there is no skyline available (or pure is true), just
for (vsize i = elements->size (); i--;)
{
Grob *g = (*elements)[i];
- Interval extent = g->maybe_pure_extent (g, a, pure, start, end);
- Interval other_extent = pure ? Interval (-infinity_f, infinity_f)
- : g->extent (common_refpoint, other_axis (a));
- Box b;
- b[a] = extent;
- b[other_axis (a)] = other_extent;
-
- if (extent.is_empty ())
- {
- elements->erase (elements->begin () + i);
- continue;
- }
-
Skyline_pair skylines;
- if (!pure
- && Skyline_pair::unsmob (g->get_property ("skylines")))
- skylines = *Skyline_pair::unsmob (g->get_property ("skylines"));
- else
- {
- if (!pure)
- programming_error ("no skylines for alignment-child\n");
-
- skylines = Skyline_pair (b, 0, other_axis (a));
- }
/* each skyline is calculated relative to (potentially) a different other_axis
coordinate. In order to compare the skylines effectively, we need to shift them
to some absolute reference point */
if (!pure)
{
+ Skyline_pair *skys = Skyline_pair::unsmob (g->get_property ("skylines"));
+ if (skys)
+ skylines = *skys;
+ else
+ programming_error ("no skylines for alignment-child\n");
+
/* this is perhaps an abuse of minimum-?-extent: maybe we should create
another property? But it seems that the only (current) use of
minimum-Y-extent is to separate vertically-aligned elements */
SCM min_extent = g->get_property (a == X_AXIS ? "minimum-X-extent" : "minimum-Y-extent");
+
if (is_number_pair (min_extent))
{
+ Box b;
+ Interval other_extent = g->extent (common_refpoint, other_axis (a));
b[a] = ly_scm2interval (min_extent);
- skylines.insert (b, 0, other_axis (a));
+ b[other_axis (a)] = other_extent;
+ if (!other_extent.is_empty ())
+ skylines.insert (b, 0, other_axis (a));
}
Real offset = child_refpoints[i]->relative_coordinate (common_refpoint, other_axis (a));
skylines.shift (offset);
}
+ else
+ {
+ assert (a == Y_AXIS);
+ Interval extent = g->pure_height (g, start, end);
+ if (!extent.is_empty ())
+ {
+ Box b;
+ b[a] = extent;
+ b[other_axis (a)] = Interval (-infinity_f, infinity_f);
+ skylines.insert (b, 0, other_axis (a));
+ }
+ }
-
- ret->push_back (skylines);
+ if (skylines.is_empty ())
+ elements->erase (elements->begin () + i);
+ else
+ ret->push_back (skylines);
}
reverse (*ret);
}
get_skylines (me, &elems, a, pure, start, end, &skylines);
Real where = 0;
+ /* TODO: extra-space stuff belongs to two-pass spacing. Delete me */
SCM extra_space_handle = scm_assq (ly_symbol2scm ("alignment-extra-space"), line_break_details);
Real extra_space = robust_scm2double (scm_is_pair (extra_space_handle)
? scm_cdr (extra_space_handle)
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 = stretchable_children_count (me);
+ 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 ()
+ && !to_boolean (elts[i]->get_property ("keep-fixed-while-stretching")))
+ 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);
}
+int
+Align_interface::stretchable_children_count (Grob const *me)
+{
+ extract_grob_set (me, "elements", elts);
+ int ret = 0;
+
+ /* start at 1: we will never move the first child while stretching */
+ for (vsize i = 1; i < elts.size (); i++)
+ if (!to_boolean (elts[i]->get_property ("keep-fixed-while-stretching"))
+ && !elts[i]->extent (elts[i], Y_AXIS).is_empty ())
+ ret++;
+
+ return ret;
+}
+
+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 && stretchable_children_count (me) > 0)
+ {
+ 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
piano staff.
+
+ FIXME: piano staves no longer have forced-distance. The code that
+ relies on this function (in line-spanner) is broken.
*/
Grob *
find_fixed_alignment_parent (Grob *g)
{
- while (g)
- {
- if (scm_is_number (g->get_property ("forced-distance")))
- return g;
-
- g = g->get_parent (Y_AXIS);
- }
-
+ (void) g;
+ programming_error ("deprecated. We don't use forced-distance anymore");
return 0;
}
"align-dir "
"axes "
"elements "
- "forced-distance "
"padding "
"positioning-done "
"stacking-dir "
for (vsize i = 0; i < elts.size (); i++)
{
Grob *se = elts[i];
- Interval dims = se->extent (common, a);
- if (!dims.is_empty ())
- r.unite (dims);
+ if (!to_boolean (se->get_property ("cross-staff")))
+ {
+ Interval dims = se->extent (common, a);
+ if (!dims.is_empty ())
+ r.unite (dims);
+ }
}
return r;
}
else if (priority_1 > priority_2)
return false;
+ /* if neither grob has an outside-staff priority, the ordering will have no
+ effect -- we just need to choose a consistent ordering. We do this to
+ avoid the side-effect of calculating extents. */
+ if (isinf (priority_1))
+ return g1 < g2;
+
/* if there is no preference in staff priority, choose the left-most one */
Grob *common = g1->common_refpoint (g2, X_AXIS);
Real start_1 = g1->extent (common, X_AXIS)[LEFT];
for (vsize i = 0; i < elements->size (); i++)
add_boxes (elements->grob (i), x_common, y_common, boxes);
}
- else if (!scm_is_number (me->get_property ("outside-staff-priority")))
+ else if (!scm_is_number (me->get_property ("outside-staff-priority"))
+ && !to_boolean (me->get_property ("cross-staff")))
boxes->push_back (Box (me->extent (x_common, X_AXIS),
me->extent (y_common, Y_AXIS)));
}
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 "
+ "keep-fixed-while-stretching "
+ "max-stretch "
"pure-Y-common "
"pure-relevant-elements "
"skylines "
void
Bar_number_engraver::stop_translation_timestep ()
{
- if (text_)
- {
- text_->set_object ("side-support-elements",
- grob_list_to_grob_array (get_property ("stavesFound")));
- text_ = 0;
- }
+ text_ = 0;
}
void
sets stem directions, a constant shift does not have an
influence.
*/
- head_extents += stem->relative_coordinate (common, Y_AXIS);
+ head_extents += stem->pure_relative_y_coordinate (common, 0, INT_MAX);
if (to_dir (stem->get_property_data ("direction")))
{
Real dx = lvs->relative_coordinate (commonx, X_AXIS) - x0;
Drul_array<Real> pos = ly_scm2interval (posns);
-
scale_drul (&pos, Staff_symbol_referencer::staff_space (me));
return knee;
}
+bool
+Beam::is_cross_staff (Grob *me)
+{
+ extract_grob_set (me, "stems", stems);
+ Grob *staff_symbol = Staff_symbol_referencer::get_staff_symbol (me);
+ for (vsize i = 0; i < stems.size (); i++)
+ if (Staff_symbol_referencer::get_staff_symbol (stems[i]) != staff_symbol)
+ return true;
+ return false;
+}
+
+MAKE_SCHEME_CALLBACK (Beam, cross_staff, 1)
+SCM
+Beam::cross_staff (SCM smob)
+{
+ return scm_from_bool (is_cross_staff (unsmob_grob (smob)));
+}
+
int
Beam::get_direction_beam_count (Grob *me, Direction d)
{
return found_something;
}
-vector<Column_x_positions>
-Constrained_breaking::solve ()
-{
- if (!systems_)
- return get_best_solution (0, VPOS);
-
- resize (systems_);
- return get_solution(0, VPOS, systems_);
-}
-
Column_x_positions
Constrained_breaking::space_line (vsize i, vsize j)
{
}
vector<Column_x_positions>
-Constrained_breaking::get_solution (vsize start, vsize end, vsize sys_count)
+Constrained_breaking::solve (vsize start, vsize end, vsize sys_count)
{
vsize start_brk = starting_breakpoints_[start];
vsize end_brk = prepare_solution (start, end, sys_count);
}
}
/* if we get to here, just put everything on one line */
- warning (_ ("cannot find line breaking that satisfies constraints" ));
+ warning (_ ("cannot find line breaking that satisfies constraints"));
ret.push_back (space_line (0, end_brk));
return ret;
}
vector<Column_x_positions>
-Constrained_breaking::get_best_solution (vsize start, vsize end)
+Constrained_breaking::best_solution (vsize start, vsize end)
{
- vsize min_systems = get_min_systems (start, end);
- vsize max_systems = get_max_systems (start, end);
+ vsize min_systems = min_system_count (start, end);
+ vsize max_systems = max_system_count (start, end);
Real best_demerits = infinity_f;
vector<Column_x_positions> best_so_far;
if (dem < best_demerits)
{
best_demerits = dem;
- best_so_far = get_solution (start, end, i);
+ best_so_far = solve (start, end, i);
}
else
{
- vector<Column_x_positions> cur = get_solution (start, end, i);
+ vector<Column_x_positions> cur = solve (start, end, i);
bool too_many_lines = true;
for (vsize j = 0; j < cur.size (); j++)
}
if (best_so_far.size ())
return best_so_far;
- return get_solution (start, end, max_systems);
+ return solve (start, end, max_systems);
}
std::vector<Line_details>
-Constrained_breaking::get_details (vsize start, vsize end, vsize sys_count)
+Constrained_breaking::line_details (vsize start, vsize end, vsize sys_count)
{
vsize brk = prepare_solution (start, end, sys_count);
Matrix<Constrained_break_node> const &st = state_[start];
}
int
-Constrained_breaking::get_min_systems (vsize start, vsize end)
+Constrained_breaking::min_system_count (vsize start, vsize end)
{
vsize sys_count;
vsize brk = prepare_solution (start, end, 1);
}
int
-Constrained_breaking::get_max_systems (vsize start, vsize end)
+Constrained_breaking::max_system_count (vsize start, vsize end)
{
vsize brk = (end >= start_.size ()) ? breaks_.size () : starting_breakpoints_[end];
return brk - starting_breakpoints_[start];
line.force_ = forces[i*breaks_.size () + j];
if (ragged && last && !isinf (line.force_))
- line.force_ = 0;
+ line.force_ = (line.force_ < 0) ? infinity_f : 0;
if (isinf (line.force_))
break;
void typeset_all ();
TRANSLATOR_DECLARATIONS (Dynamic_engraver);
- DECLARE_ACKNOWLEDGER (accidental);
- DECLARE_ACKNOWLEDGER (stem_tremolo);
DECLARE_ACKNOWLEDGER (note_column);
- DECLARE_ACKNOWLEDGER (slur);
DECLARE_TRANSLATOR_LISTENER (absolute_dynamic);
DECLARE_TRANSLATOR_LISTENER (span_dynamic);
}
}
-void
-Dynamic_engraver::acknowledge_accidental (Grob_info info)
-{
- if (line_spanner_)
- Side_position_interface::add_support (line_spanner_, info.grob ());
-}
-
-void
-Dynamic_engraver::acknowledge_stem_tremolo (Grob_info info)
-{
- if (line_spanner_)
- Side_position_interface::add_support (line_spanner_, info.grob ());
-}
-
-void
-Dynamic_engraver::acknowledge_slur (Grob_info info)
-{
- if (line_spanner_)
- Side_position_interface::add_support (line_spanner_, info.grob ());
-}
-
void
Dynamic_engraver::acknowledge_note_column (Grob_info info)
{
finished_cresc_->set_bound (RIGHT, info.grob ());
}
-ADD_ACKNOWLEDGER (Dynamic_engraver, accidental);
ADD_ACKNOWLEDGER (Dynamic_engraver, note_column);
-ADD_ACKNOWLEDGER (Dynamic_engraver, slur);
-ADD_ACKNOWLEDGER (Dynamic_engraver, stem_tremolo);
ADD_TRANSLATOR (Dynamic_engraver,
/* doc */
"before-line-breaking "
"cause "
"color "
+ "cross-staff "
"extra-X-extent "
"extra-Y-extent "
"extra-offset "
{
DECLARE_SCHEME_CALLBACK (calc_positioning_done, (SCM));
DECLARE_SCHEME_CALLBACK (stretch_after_break, (SCM element));
- static void align_to_fixed_distance (Grob *, Axis a);
+ DECLARE_SCHEME_CALLBACK (calc_max_stretch, (SCM));
+ static void stretch (Grob *, Real amount, Axis a);
static void align_elements_to_extents (Grob *, Axis a);
static vector<Real> get_extents_aligned_translates (Grob *, vector<Grob*> const&,
Axis a,
bool safe, int start, int end);
+ static int stretchable_children_count (Grob const*);
static void set_ordered (Grob *);
static Axis axis (Grob *);
static void add_element (Grob *, Grob *);
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,
static Grob *last_normal_stem (Grob *);
DECLARE_GROB_INTERFACE();
static void add_stem (Grob *, Grob *);
+ static bool is_cross_staff (Grob *);
static bool is_knee (Grob *);
static void set_beaming (Grob *, Beaming_pattern const *);
static void set_stemlens (Grob *);
DECLARE_SCHEME_CALLBACK (calc_normal_stems, (SCM));
DECLARE_SCHEME_CALLBACK (calc_concaveness, (SCM));
DECLARE_SCHEME_CALLBACK (set_stem_lengths, (SCM));
+ DECLARE_SCHEME_CALLBACK (cross_staff, (SCM));
/* position callbacks */
DECLARE_SCHEME_CALLBACK (shift_region_to_valid, (SCM, SCM));
class Constrained_breaking
{
public:
- vector<Column_x_positions> solve ();
+ vector<Column_x_positions> solve (vsize start, vsize end, vsize sys_count);
+ vector<Column_x_positions> best_solution (vsize start, vsize end);
+ vector<Line_details> line_details (vsize start, vsize end, vsize sys_count);
+
Constrained_breaking (Paper_score *ps);
Constrained_breaking (Paper_score *ps, vector<vsize> const &start_col_posns);
- vector<Column_x_positions> get_solution (vsize start, vsize end, vsize sys_count);
- vector<Column_x_positions> get_best_solution (vsize start, vsize end);
- vector<Line_details> get_details (vsize start, vsize end, vsize sys_count);
- int get_max_systems (vsize start, vsize end);
- int get_min_systems (vsize start, vsize end);
-
- void resize (vsize systems);
+ int max_system_count (vsize start, vsize end);
+ int min_system_count (vsize start, vsize end);
private:
Paper_score *pscore_;
vector<vsize> breaks_;
void initialize ();
+ void resize (vsize systems);
Column_x_positions space_line (vsize start_col, vsize end_col);
vsize prepare_solution (vsize start, vsize end, vsize sys_count);
SCM get_property_alist_chain (SCM) const;
SCM internal_get_property (SCM symbol) const;
SCM internal_get_property_data (SCM symbol) const;
+ SCM internal_get_non_callback_marker_property_data (SCM symbol) const;
SCM internal_get_object (SCM symbol) const;
void internal_set_object (SCM sym, SCM val);
void internal_del_property (SCM symbol);
vector<Real> spring_positions () const;
Real force () const;
+ Real force_penalty (bool ragged) const;
bool fits () const;
DECLARE_SIMPLE_SMOBS (Simple_spacer);
Real height (Real airplane) const;
Real max_height () const;
void set_minimum_height (Real height);
+ bool is_empty () const;
};
class Skyline_pair
void merge (Skyline_pair const &other);
Skyline &operator [] (Direction d);
Skyline const &operator [] (Direction d) const;
+ bool is_empty () const;
};
#endif /* SKYLINE_HH */
DECLARE_SCHEME_CALLBACK (height, (SCM));
DECLARE_SCHEME_CALLBACK (outside_slur_callback, (SCM, SCM));
DECLARE_SCHEME_CALLBACK (pure_outside_slur_callback, (SCM, SCM, SCM, SCM));
+ DECLARE_SCHEME_CALLBACK (cross_staff, (SCM));
DECLARE_GROB_INTERFACE();
static Bezier get_curve (Grob *me);
};
static int head_count (Grob *);
static bool is_invisible (Grob *);
static bool is_normal_stem (Grob *);
+ static bool is_cross_staff (Grob *);
static Interval head_positions (Grob *);
static Real stem_end_position (Grob *);
static Stencil flag (Grob *);
DECLARE_SCHEME_CALLBACK (width, (SCM smob));
DECLARE_SCHEME_CALLBACK (pure_height, (SCM, SCM, SCM));
DECLARE_SCHEME_CALLBACK (height, (SCM));
+ DECLARE_SCHEME_CALLBACK (cross_staff, (SCM));
};
#endif
{
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 &);
DECLARE_GROB_INTERFACE();
DECLARE_SCHEME_CALLBACK (print, (SCM));
static void modify_edge_height (Spanner *);
- static void add_column (Grob *, Grob *col);
static void add_bar (Grob *me, Item *bar);
};
void
Mark_engraver::stop_translation_timestep ()
{
- if (text_)
- {
- text_->set_object ("side-support-elements",
- grob_list_to_grob_array (get_property ("stavesFound")));
- text_ = 0;
- }
+ text_ = 0;
mark_ev_ = 0;
}
{
Grob *mc = unsmob_grob (get_property ("currentMusicalColumn"));
text_->set_parent (mc, X_AXIS);
- text_->set_object ("side-support-elements",
- grob_list_to_grob_array (get_property ("stavesFound")));
-
text_ = 0;
}
}
line_breaker_args (sys, chunks[i], chunks[i+1], &start, &end);
vector<Column_x_positions> pos = ignore_div
- ? line_breaking_[sys].get_best_solution (start, end)
- : line_breaking_[sys].get_solution (start, end, div[i]);
+ ? line_breaking_[sys].best_solution (start, end)
+ : line_breaking_[sys].solve (start, end, div[i]);
all_[sys].pscore_->root_system ()->break_into_pieces (pos);
}
}
{
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
{
vsize end;
line_breaker_args (sys, chunks[i], chunks[i+1], &start, &end);
- vector<Line_details> details = line_breaking_[sys].get_details (start, end, div[i]);
+ vector<Line_details> details = line_breaking_[sys].line_details (start, end, div[i]);
ret.insert (ret.end (), details.begin (), details.end ());
}
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);
vsize end;
line_breaker_args (sys, chunks[i], chunks[i+1], &start, &end);
ret[i] = min
- ? line_breaking_[sys].get_min_systems (start, end)
- : line_breaking_[sys].get_max_systems (start, end);
+ ? line_breaking_[sys].min_system_count (start, end)
+ : line_breaking_[sys].max_system_count (start, end);
}
}
int system_count = robust_scm2int (layout ()->c_variable ("system-count"), 0);
if (system_count)
- algorithm.resize (system_count);
+ return algorithm.solve (0, VPOS, system_count);
- return algorithm.solve ();
+ return algorithm.best_solution (0, VPOS);
}
void
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);
+}
return ret;
}
+Real
+Simple_spacer::force_penalty (bool ragged) const
+{
+ /* If we are ragged-right, we don't want to penalise according to the force,
+ but according to the amount of whitespace that is present after the end
+ of the line. */
+ if (ragged)
+ return max (0.0, line_len_ - configuration_length (0.0));
+
+ /* Use a convex compression penalty. */
+ Real f = force_;
+ return f - (f < 0 ? f*f*f*f*4 : 0);
+}
+
/****************************************************************/
Spring_description::Spring_description ()
}
}
spacer.solve ((b == 0) ? line_len - indent : line_len, ragged);
-
- /* add a (convex) penalty for compression. We do this _only_ in get_line_forces,
- not get_line_configuration. This is temporary, for backwards compatibility;
- the old line/page-breaking stuff ignores page breaks when it calculates line
- breaks, so compression penalties can result in scores (eg. wtk-fugue) blowing
- up to too many pages. */
- Real f = spacer.force ();
- force[b * breaks.size () + c] = f - (f < 0 ? f*f*f*f*4 : 0);
+ force[b * breaks.size () + c] = spacer.force_penalty (ragged);
if (!spacer.fits ())
{
}
spacer.solve (line_len, ragged);
- ret.force_ = spacer.force ();
+ ret.force_ = spacer.force_penalty (ragged);
- /*
- We used to have a penalty for compression, no matter what, but that
- fucked up wtk1-fugue2 (taking 3 full pages.)
- */
ret.config_ = spacer.spring_positions ();
for (vsize i = 0; i < ret.config_.size (); i++)
ret.config_[i] += indent;
return out;
}
+bool
+Skyline::is_empty () const
+{
+ return buildings_.empty ();
+}
+
Skyline_pair::Skyline_pair ()
: skylines_ (Skyline (DOWN), Skyline (UP))
{
skylines_[DOWN].merge (other[DOWN]);
}
+bool
+Skyline_pair::is_empty () const
+{
+ return skylines_[UP].is_empty ()
+ && skylines_[DOWN].is_empty ();
+}
+
Skyline&
Skyline_pair::operator [] (Direction d)
{
e->warning ("Ignoring grob for slur. avoid-slur not set?");
}
+MAKE_SCHEME_CALLBACK (Slur, cross_staff, 1)
+SCM
+Slur::cross_staff (SCM smob)
+{
+ Grob *me = unsmob_grob (smob);
+ Grob *staff = Staff_symbol_referencer::get_staff_symbol (me);
+ assert (staff); // delete me
+ extract_grob_set (me, "note-columns", cols);
+
+ for (vsize i = 0; i < cols.size (); i++)
+ if (Staff_symbol_referencer::get_staff_symbol (cols[i]) != staff)
+ return SCM_BOOL_T;
+ return SCM_BOOL_F;
+}
ADD_INTERFACE (Slur,
if (!is_normal_stem (me))
return ly_interval2scm (iv);
+
+ /* if we are part of a cross-staff beam, return empty */
+ if (get_beam (me) && Beam::is_cross_staff (get_beam (me)))
+ return ly_interval2scm (iv);
Real ss = Staff_symbol_referencer::staff_space (me);
Real len = scm_to_double (calc_length (smob)) * ss / 2;
return le;
}
+bool
+Stem::is_cross_staff (Grob *stem)
+{
+ Grob *beam = unsmob_grob (stem->get_object ("beam"));
+ return beam && Beam::is_cross_staff (beam);
+}
+
+MAKE_SCHEME_CALLBACK (Stem, cross_staff, 1)
+SCM
+Stem::cross_staff (SCM smob)
+{
+ return scm_from_bool (is_cross_staff (unsmob_grob (smob)));
+}
+
/* FIXME: Too many properties */
ADD_INTERFACE (Stem,
"The stem represent the graphical stem. "
--- /dev/null
+/*
+ system-scheme.cc -- implement scheme bindings to System
+
+ source file of the GNU LilyPond music typesetter
+
+ (c) 2007 Joe Neeman <joeneeman@gmail.com>
+*/
+
+#include "align-interface.hh"
+#include "pointer-group-interface.hh"
+#include "system.hh"
+
+
+LY_DEFINE (ly_system_print, "ly:system-print",
+ 1, 0, 0, (SCM system),
+ "Draw the system and return the prob containing its "
+ "stencil.")
+{
+ Grob *me = unsmob_grob (system);
+ System *me_system = dynamic_cast<System*> (me);
+ SCM_ASSERT_TYPE (me, system, SCM_ARG1, __FUNCTION__, "grob");
+
+ return me_system->get_paper_system ();
+}
+
+LY_DEFINE (ly_system_stretch, "ly:system-stretch",
+ 2, 0, 0, (SCM system, SCM amount_scm),
+ "Stretch the system vertically by the given amount. "
+ "This must be called before the system is drawn (for example "
+ "with ly:system-print).")
+{
+ Grob *me = unsmob_grob (system);
+ Real amount = robust_scm2double (amount_scm, 0.0);
+
+ extract_grob_set (me, "elements", elts);
+ for (vsize i = 0; i < elts.size (); i++)
+ if (Align_interface::has_interface (elts[i]))
+ {
+ Align_interface::stretch (elts[i], amount, Y_AXIS);
+ break;
+ }
+ return SCM_UNDEFINED;
+}
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));
- }
+ 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 ();
void process_acknowledged ();
DECLARE_TRANSLATOR_LISTENER (text_script);
- DECLARE_ACKNOWLEDGER (stem_tremolo);
- DECLARE_ACKNOWLEDGER (stem);
- DECLARE_ACKNOWLEDGER (rhythmic_head);
};
IMPLEMENT_TRANSLATOR_LISTENER (Text_engraver, text_script);
evs_.push_back (ev);
}
-void
-Text_engraver::acknowledge_rhythmic_head (Grob_info inf)
-{
- for (vsize i = 0; i < texts_.size (); i++)
- {
- Grob *t = texts_[i];
- Side_position_interface::add_support (t, inf.grob ());
-
- /*
- ugh.
- */
- if (Side_position_interface::get_axis (t) == X_AXIS
- && !t->get_parent (Y_AXIS))
- t->set_parent (inf.grob (), Y_AXIS);
- else if (Side_position_interface::get_axis (t) == Y_AXIS
- && !t->get_parent (X_AXIS))
- t->set_parent (inf.grob (), X_AXIS);
- }
-}
-
-void
-Text_engraver::acknowledge_stem (Grob_info inf)
-{
- for (vsize i = 0; i < texts_.size (); i++)
- Side_position_interface::add_support (texts_[i], inf.grob ());
-}
-
-void
-Text_engraver::acknowledge_stem_tremolo (Grob_info info)
-{
- for (vsize i = 0; i < texts_.size (); i++)
- Side_position_interface::add_support (texts_[i], info.grob ());
-}
-
void
Text_engraver::process_acknowledged ()
{
{
}
-ADD_ACKNOWLEDGER (Text_engraver, stem);
-ADD_ACKNOWLEDGER (Text_engraver, stem_tremolo);
-ADD_ACKNOWLEDGER (Text_engraver, rhythmic_head);
ADD_TRANSLATOR (Text_engraver,
/* doc */ "Create text-scripts",
/* create */ "TextScript",
TRANSLATOR_DECLARATIONS (Text_spanner_engraver);
protected:
virtual void finalize ();
- DECLARE_ACKNOWLEDGER (note_column);
DECLARE_TRANSLATOR_LISTENER (text_span);
void stop_translation_timestep ();
void process_music ();
}
}
-void
-Text_spanner_engraver::acknowledge_note_column (Grob_info info)
-{
- Spanner *spans[2] ={span_, finished_};
- for (int i = 0; i < 2; i++)
- {
- if (spans[i])
- {
- Side_position_interface::add_support (spans[i], info.grob ());
- add_bound_item (spans[i], info.grob ());
- }
- }
-}
-
void
Text_spanner_engraver::typeset_all ()
{
}
}
-ADD_ACKNOWLEDGER (Text_spanner_engraver, note_column);
-
ADD_TRANSLATOR (Text_spanner_engraver,
/* doc */
"Create text spanner from an event.",
x.add_point (stem->relative_coordinate (x_refpoint_, X_AXIS));
x.widen (staff_space / 20); // ugh.
Interval y;
- y.add_point (Stem::stem_end_position (stem) * staff_space * .5);
+ Real stem_end_position =
+ Stem::is_cross_staff (stem)
+ ? get_grob_direction (stem) * infinity_f
+ : Stem::stem_end_position (stem) * staff_space * .5;
+
+ y.add_point (stem_end_position);
Direction stemdir = get_grob_direction (stem);
y.add_point (Stem::head_positions (stem)[-stemdir]
TRANSLATOR_DECLARATIONS (Trill_spanner_engraver);
protected:
virtual void finalize ();
- DECLARE_ACKNOWLEDGER (note_column);
DECLARE_TRANSLATOR_LISTENER (trill_span);
void stop_translation_timestep ();
void process_music ();
}
}
-void
-Trill_spanner_engraver::acknowledge_note_column (Grob_info info)
-{
- Spanner *spans[2] ={span_, finished_};
- for (int i = 0; i < 2; i++)
- {
- if (spans[i])
- {
- Side_position_interface::add_support (spans[i], info.grob ());
- add_bound_item (spans[i], info.grob ());
- }
- }
-}
-
void
Trill_spanner_engraver::typeset_all ()
{
}
}
-ADD_ACKNOWLEDGER (Trill_spanner_engraver, note_column);
ADD_TRANSLATOR (Trill_spanner_engraver,
/* doc */ "Create trill spanner from an event.",
/* create */
add_bound_item (dynamic_cast<Spanner *> (me), b);
}
-void
-Volta_bracket_interface::add_column (Grob *me, Grob *c)
-{
- Side_position_interface::add_support (me, c);
-}
-
ADD_INTERFACE (Volta_bracket_interface,
"Volta bracket with number",
DECLARE_END_ACKNOWLEDGER (staff_symbol);
DECLARE_ACKNOWLEDGER (staff_symbol);
- DECLARE_ACKNOWLEDGER (note_column);
DECLARE_ACKNOWLEDGER (bar_line);
virtual void finalize ();
}
}
-void
-Volta_engraver::acknowledge_note_column (Grob_info i)
-{
- if (volta_span_)
- Volta_bracket_interface::add_column (volta_span_, i.grob ());
-}
-
void
Volta_engraver::acknowledge_bar_line (Grob_info i)
{
*/
ADD_ACKNOWLEDGER (Volta_engraver, staff_symbol);
ADD_END_ACKNOWLEDGER (Volta_engraver, staff_symbol);
-ADD_ACKNOWLEDGER (Volta_engraver, note_column);
ADD_ACKNOWLEDGER (Volta_engraver, bar_line);
ADD_TRANSLATOR (Volta_engraver,
/* doc */ "Make volta brackets.",
\consists "Figured_bass_position_engraver"
\consists "Script_row_engraver"
- \override VerticalAxisGroup #'minimum-Y-extent = #'(-3.5 . 3.5)
localKeySignature = #'()
createSpacing = ##t
ignoreFiguredBassRest = ##t
\description
"Just like @code{GrandStaff} but with a forced distance between
the staves, so cross staff beaming and slurring can be used."
-
- \override VerticalAlignment #'forced-distance = #12
- \override VerticalAlignment #'self-alignment-Y = #0
- \consists "Vertical_align_engraver"
+ \override VerticalAxisGroup #'minimum-Y-extent = #'(-4 . 4)
\consists "Instrument_name_engraver"
instrumentName = #'()
\override VerticalAxisGroup #'remove-first = ##t
\override VerticalAxisGroup #'remove-empty = ##t
+ \override VerticalAxisGroup #'keep-fixed-while-stretching = ##t
\override SeparationItem #'padding = #0.2
\override InstrumentName #'self-alignment-Y = ##f
(font-shape ,symbol? "Select the shape of a font. Choices include @code{upright},
@code{italic}, @code{caps}.")
(forced ,boolean? "manually forced accidental")
- (forced-distance ,ly:dimension? "A fixed distance between object
-reference points in an alignment.")
(force-hshift ,number? "This specifies a manual shift for notes
in collisions. The unit is the note head width of the first voice
note. This is used by @internalsref{note-collision-interface}.")
(inspect-index ,integer? "If debugging is set,
set beam/slur configuration to this index, and print the respective scores.")
(implicit ,boolean? "Is this an implicit bass figure?")
+ (keep-fixed-while-stretching ,boolean? "A grob with this property set to true will be fixed relative to the staff above it when systems are stretched.")
(keep-inside-line ,boolean? "If set, this column cannot have
things sticking into the margin.")
(kern ,ly:dimension? "Amount of extra white space to add. For
(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
(arpeggio ,ly:grob? "pointer to arpeggio object.")
(beam ,ly:grob? "pointer to the beam, if applicable.")
(bracket ,ly:grob? "the bracket for a number.")
+ (cross-staff ,boolean? "for a beam or a stem, true if we depend on inter-staff spacing")
(direction-source ,ly:grob? "in case side-relative-direction is
set, which grob to get the direction from .")
(dot ,ly:grob? "reference to Dots object.")
(beaming . ,ly:beam::calc-beaming)
(stencil . ,ly:beam::print)
(clip-edges . #t)
+ (cross-staff . ,ly:beam::cross-staff)
(details . ((hint-direction-penalty . 20)))
;; TODO: should be in SLT.
(Y-offset . ,ly:side-position-interface::y-aligned-side)
(staff-padding . 0.1)
(padding . 0.6)
- (avoid-slur . outside)
(slur-padding . 0.3)
(minimum-space . 1.2)
(direction . ,DOWN)
(font-series . bold)
(font-encoding . fetaDynamic)
(font-shape . italic)
- (avoid-slur . around)
(extra-spacing-width . (+inf.0 . -inf.0))
(outside-staff-priority . 250)
(meta . ((class . Item)
(minimum-length . 1.5)
(height-limit . 2.0)
(ratio . 0.333)
+ (cross-staff . ,ly:slur::cross-staff)
(meta . ((class . Spanner)
(interfaces . (slur-interface))))))
(height-limit . 2.0)
(ratio . 0.25)
(avoid-slur . inside)
+ (cross-staff . ,ly:slur::cross-staff)
(meta . ((class . Spanner)
(interfaces . (slur-interface))))))
(Y-extent . ,ly:stem::height)
(length . ,ly:stem::calc-length)
(thickness . 1.3)
+ (cross-staff . ,ly:stem::cross-staff)
(details
. (
;; 3.5 (or 3 measured from note head) is standard length
(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
#:use-module (lily)
#:export (post-process-pages optimal-page-breaks make-page-from-systems
page-breaking-wrapper
+ stretchable-line? ; delete me
;; utilities for writing custom page breaking functions
line-height line-next-space line-next-padding
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))
+; this is for 2-pass spacing. Delete me.
+(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 (stretch-and-draw-page paper-book systems page-number ragged last)
+ (define (max-stretch sys)
+ (if (ly:grob? sys)
+ (ly:grob-property sys 'max-stretch)
+ 0.0))
+
+ (define (stretchable? sys)
+ (and (ly:grob? sys)
+ (> (max-stretch sys) 0.0)))
+
+ (define (height-estimate sys)
+ (interval-length
+ (if (ly:grob? sys)
+ (ly:grob-property sys 'pure-Y-extent)
+ (paper-system-extent sys Y))))
+
+ (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