/*
- axis-group-interface.cc -- implement Axis_group_interface
+ This file is part of LilyPond, the GNU music typesetter.
- source file of the GNU LilyPond music typesetter
+ Copyright (C) 2000--2010 Han-Wen Nienhuys <hanwen@xs4all.nl>
- (c) 2000--2009 Han-Wen Nienhuys <hanwen@xs4all.nl>
+ LilyPond is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ LilyPond is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with LilyPond. If not, see <http://www.gnu.org/licenses/>.
*/
#include "axis-group-interface.hh"
#include "system.hh"
#include "warn.hh"
+static bool
+pure_staff_priority_less (Grob *const &g1, Grob *const &g2);
+
void
Axis_group_interface::add_element (Grob *me, Grob *e)
{
return ext;
}
+
// adjacent-pure-heights is a pair of vectors, each of which has one element
// for every measure in the score. The first vector stores, for each measure,
// the combined height of the elements that are present only when the bar
{
Grob *me = unsmob_grob (smob);
- Grob *common = calc_pure_elts_and_common (me);
- extract_grob_set (me, "pure-relevant-items", items);
- extract_grob_set (me, "pure-relevant-spanners", spanners);
+ Grob *common = unsmob_grob (me->get_object ("pure-Y-common"));
+ extract_grob_set (me, "pure-relevant-grobs", elts);
Paper_score *ps = get_root_system (me)->paper_score ();
- vector<vsize> breaks = ps->get_break_indices ();
- vector<Grob*> cols = ps->get_columns ();
+ vector<vsize> ranks = ps->get_break_ranks ();
- SCM begin_line_heights = scm_c_make_vector (breaks.size () - 1, SCM_EOL);
- SCM mid_line_heights = scm_c_make_vector (breaks.size () - 1, SCM_EOL);
+ vector<Interval> begin_line_heights;
+ vector<Interval> mid_line_heights;
+ begin_line_heights.resize (ranks.size () - 1);
+ mid_line_heights.resize (ranks.size () - 1);
- vsize it_index = 0;
- for (vsize i = 0; i + 1 < breaks.size (); i++)
+ for (vsize i = 0; i < elts.size (); ++i)
{
- int start = Paper_column::get_rank (cols[breaks[i]]);
- int end = Paper_column::get_rank (cols[breaks[i+1]]);
- Interval begin_line_iv;
- Interval mid_line_iv;
+ Grob *g = elts[i];
- for (vsize j = it_index; j < items.size (); j++)
- {
- Item *it = dynamic_cast<Item*> (items[j]);
- int rank = it->get_column ()->get_rank ();
+ if (to_boolean (g->get_property ("cross-staff")))
+ continue;
- if (rank <= end && it->pure_is_visible (start, end)
- && !to_boolean (it->get_property ("cross-staff")))
- {
- Interval dims = items[j]->pure_height (common, start, end);
- Interval &target_iv = start == it->get_column ()->get_rank () ? begin_line_iv : mid_line_iv;
+ bool outside_staff = scm_is_number (g->get_property ("outside-staff-priority"));
+ Real padding = robust_scm2double (g->get_property ("outside-staff-padding"), 0.5);
- if (!dims.is_empty ())
- target_iv.unite (dims);
- }
+ // TODO: consider a pure version of get_grob_direction?
+ Direction d = to_dir (g->get_property_data ("direction"));
+ d = (d == CENTER) ? UP : d;
- if (rank < end)
- it_index++;
- else if (rank > end)
- break;
- }
+ Interval_t<int> rank_span = g->spanned_rank_interval ();
+ vsize first_break = lower_bound (ranks, (vsize)rank_span[LEFT], less<vsize> ());
+ if (first_break > 0 && ranks[first_break] >= (vsize)rank_span[LEFT])
+ first_break--;
- for (vsize j = 0; j < spanners.size (); j++)
+ for (vsize j = first_break; j+1 < ranks.size () && (int)ranks[j] <= rank_span[RIGHT]; ++j)
{
- Interval_t<int> rank_span = spanners[j]->spanned_rank_interval ();
- if (rank_span[LEFT] <= end && rank_span[RIGHT] >= start
- && !to_boolean (spanners[j]->get_property ("cross-staff")))
- {
- Interval dims = spanners[j]->pure_height (common, start, end);
+ int start = ranks[j];
+ int end = ranks[j+1];
+ // Take grobs that are visible with respect to a slightly longer line.
+ // Otherwise, we will never include grobs at breakpoints which aren't
+ // end-of-line-visible.
+ int visibility_end = j + 2 < ranks.size () ? ranks[j+2] : end;
+
+ if (g->pure_is_visible (start, visibility_end))
+ {
+ Interval dims = g->pure_height (common, start, end);
if (!dims.is_empty ())
- mid_line_iv.unite (dims);
+ {
+ if (rank_span[LEFT] <= start)
+ {
+ if (outside_staff)
+ begin_line_heights[j].unite_disjoint (dims, padding, d);
+ else
+ begin_line_heights[j].unite (dims);
+ }
+ if (rank_span[RIGHT] > start)
+ {
+ if (outside_staff)
+ mid_line_heights[j].unite_disjoint (dims, padding, d);
+ else
+ mid_line_heights[j].unite (dims);
+ }
+ }
}
}
+ }
- scm_vector_set_x (begin_line_heights, scm_from_int (i), ly_interval2scm (begin_line_iv));
- scm_vector_set_x (mid_line_heights, scm_from_int (i), ly_interval2scm (mid_line_iv));
+ // Convert begin_line_heights and min_line_heights to SCM.
+ SCM begin_scm = scm_c_make_vector (ranks.size () - 1, SCM_EOL);
+ SCM mid_scm = scm_c_make_vector (ranks.size () - 1, SCM_EOL);
+ for (vsize i = 0; i < begin_line_heights.size (); ++i)
+ {
+ scm_vector_set_x (begin_scm, scm_from_int (i), ly_interval2scm (begin_line_heights[i]));
+ scm_vector_set_x (mid_scm, scm_from_int (i), ly_interval2scm (mid_line_heights[i]));
}
- return scm_cons (begin_line_heights, mid_line_heights);
+
+ return scm_cons (begin_scm, mid_scm);
}
Interval
Unfortunately, it isn't always true, particularly if there is a
VerticalAlignment somewhere in the descendants.
- Apart from PianoStaff, which has a fixed VerticalAlignment so it doesn't
- count, the only VerticalAlignment comes from Score. This makes it
+ Usually, the only VerticalAlignment comes from Score. This makes it
reasonably safe to assume that if our parent is a VerticalAlignment,
we can assume additivity and cache things nicely. */
Grob *p = me->get_parent (Y_AXIS);
if (p && Align_interface::has_interface (p))
return Axis_group_interface::cached_pure_height (me, start, end);
- Grob *common = calc_pure_elts_and_common (me);
- extract_grob_set (me, "pure-relevant-items", items);
- extract_grob_set (me, "pure-relevant-spanners", spanners);
+ Grob *common = unsmob_grob (me->get_object ("pure-Y-common"));
+ extract_grob_set (me, "pure-relevant-grobs", elts);
Interval r;
-
- for (vsize i = 0; i < items.size (); i++)
- {
- Item *it = dynamic_cast<Item*> (items[i]);
- int rank = it->get_column ()->get_rank ();
-
- if (rank > end)
- break;
- else if (rank >= start && it->pure_is_visible (start, end)
- && !to_boolean (it->get_property ("cross-staff")))
- {
- Interval dims = it->pure_height (common, start, end);
- if (!dims.is_empty ())
- r.unite (dims);
- }
- }
-
- for (vsize i = 0; i < spanners.size (); i++)
+ for (vsize i = 0; i < elts.size (); i++)
{
- Interval_t<int> rank_span = spanners[i]->spanned_rank_interval ();
+ Grob *g = elts[i];
+ Interval_t<int> rank_span = g->spanned_rank_interval ();
if (rank_span[LEFT] <= end && rank_span[RIGHT] >= start
- && !to_boolean (spanners[i]->get_property ("cross-staff")))
+ && g->pure_is_visible (start, end)
+ && !to_boolean (g->get_property ("cross-staff")))
{
- Interval dims = spanners[i]->pure_height (common, start, end);
+ Interval dims = g->pure_height (common, start, end);
if (!dims.is_empty ())
r.unite (dims);
}
}
-Grob *
-Axis_group_interface::calc_pure_elts_and_common (Grob *me)
+MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_pure_relevant_grobs, 1);
+SCM
+Axis_group_interface::calc_pure_relevant_grobs (SCM smob)
{
- if (Grob *c = unsmob_grob (me->get_object ("pure-Y-common")))
- return c;
+ Grob *me = unsmob_grob (smob);
extract_grob_set (me, "elements", elts);
- vector<Grob*> relevant_items;
- vector<Grob*> relevant_spanners;
+ vector<Grob*> relevant_grobs;
SCM pure_relevant_p = ly_lily_module_constant ("pure-relevant?");
for (vsize i = 0; i < elts.size (); i++)
{
if (to_boolean (scm_apply_1 (pure_relevant_p, elts[i]->self_scm (), SCM_EOL)))
+ relevant_grobs.push_back (elts[i]);
+
+ if (Item *it = dynamic_cast<Item*> (elts[i]))
{
- if (dynamic_cast<Item*> (elts[i]))
- relevant_items.push_back (elts[i]);
- else if (dynamic_cast<Spanner*> (elts[i]))
- relevant_spanners.push_back (elts[i]);
+ Direction d = LEFT;
+ do
+ {
+ Item *piece = it->find_prebroken_piece (d);
+ if (piece && to_boolean (scm_apply_1 (pure_relevant_p, piece->self_scm (), SCM_EOL)))
+ relevant_grobs.push_back (piece);
+ }
+ while (flip (&d) != LEFT);
}
-
-
- Item *it = dynamic_cast<Item*> (elts[i]);
- Direction d = LEFT;
- if (it)
- do
- {
- Item *piece = it->find_prebroken_piece (d);
- if (piece && to_boolean (scm_apply_1 (pure_relevant_p, piece->self_scm (), SCM_EOL)))
- relevant_items.push_back (piece);
- }
- while (flip (&d) != LEFT);
}
- vector_sort (relevant_items, Item::less);
- Grob *common = common_refpoint_of_array (relevant_items, me, Y_AXIS);
- common = common_refpoint_of_array (relevant_spanners, common, Y_AXIS);
+ vector_sort (relevant_grobs, pure_staff_priority_less);
+ SCM grobs_scm = Grob_array::make_array ();
+ unsmob_grob_array (grobs_scm)->set_array (relevant_grobs);
- me->set_object ("pure-Y-common", common->self_scm ());
-
- SCM items_scm = Grob_array::make_array ();
- SCM spanners_scm = Grob_array::make_array ();
+ return grobs_scm;
+}
- unsmob_grob_array (items_scm)->set_array (relevant_items);
- unsmob_grob_array (spanners_scm)->set_array (relevant_spanners);
- me->set_object ("pure-relevant-items", items_scm);
- me->set_object ("pure-relevant-spanners", spanners_scm);
+MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_pure_y_common, 1);
+SCM
+Axis_group_interface::calc_pure_y_common (SCM smob)
+{
+ Grob *me = unsmob_grob (smob);
+
+ extract_grob_set (me, "pure-relevant-grobs", elts);
+ Grob *common = common_refpoint_of_array (elts, me, Y_AXIS);
+ if (!common)
+ {
+ me->programming_error ("No common parent found in calc_pure_y_common.");
+ return SCM_EOL;
+ }
- return common;
+ return common->self_scm ();
}
SCM
me->programming_error ("No common parent found in calc_common axis.");
return SCM_EOL;
}
-
+
return common->self_scm ();
}
Interval
Axis_group_interface::pure_group_height (Grob *me, int start, int end)
{
- Grob *common = calc_pure_elts_and_common (me);
-
+ Grob *common = unsmob_grob (me->get_object ("pure-Y-common"));
+
+ if (!common)
+ {
+ programming_error ("no pure Y common refpoint");
+ return Interval ();
+ }
Real my_coord = me->relative_coordinate (common, Y_AXIS);
Interval r (relative_pure_height (me, start, end));
}
}
-bool
+static bool
staff_priority_less (Grob * const &g1, Grob * const &g2)
{
Real priority_1 = robust_scm2double (g1->get_property ("outside-staff-priority"), -infinity_f);
return start_1 < start_2;
}
+static bool
+pure_staff_priority_less (Grob * const &g1, Grob * const &g2)
+{
+ Real priority_1 = robust_scm2double (g1->get_property ("outside-staff-priority"), -infinity_f);
+ Real priority_2 = robust_scm2double (g2->get_property ("outside-staff-priority"), -infinity_f);
+
+ return priority_1 < priority_2;
+}
+
static void
add_boxes (Grob *me, Grob *x_common, Grob *y_common, vector<Box> *const boxes, Skyline_pair *skylines)
{
return ret.smobbed_copy ();
}
+MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_pure_next_staff_spacing, 3)
+SCM
+Axis_group_interface::calc_pure_next_staff_spacing (SCM smob, SCM start, SCM end)
+{
+ return calc_maybe_pure_next_staff_spacing (unsmob_grob (smob),
+ true,
+ scm_to_int (start),
+ scm_to_int (end));
+}
+
MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_next_staff_spacing, 1)
SCM
Axis_group_interface::calc_next_staff_spacing (SCM smob)
{
- Grob *me = unsmob_grob (smob);
+ return calc_maybe_pure_next_staff_spacing (unsmob_grob (smob),
+ false,
+ 0,
+ INT_MAX);
+}
+
+SCM
+Axis_group_interface::calc_maybe_pure_next_staff_spacing (Grob *me, bool pure, int start, int end)
+{
Grob *grouper = unsmob_grob (me->get_object ("staff-grouper"));
if (grouper)
{
- Grob *last_in_group = Staff_grouper_interface::get_last_grob (grouper);
+ Grob *last_in_group = Staff_grouper_interface::get_maybe_pure_last_grob (grouper, pure, start, end);
if (me == last_in_group)
- return grouper->get_property ("after-last-staff-spacing");
+ return grouper->get_maybe_pure_property ("after-last-staff-spacing", pure, start, end);
else
- return grouper->get_property ("between-staff-spacing");
+ return grouper->get_maybe_pure_property ("between-staff-spacing", pure, start, end);
}
- return me->get_property ("default-next-staff-spacing");
+ return me->get_maybe_pure_property ("default-next-staff-spacing", pure, start, end);
}
Real
"elements "
"inter-loose-line-spacing "
"inter-staff-spacing "
- "keep-fixed-while-stretching "
"max-stretch "
+ "non-affinity-spacing "
"next-staff-spacing "
"no-alignment "
"pure-Y-common "
+ "pure-relevant-grobs "
"pure-relevant-items "
"pure-relevant-spanners "
"staff-affinity "