>>
\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)
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 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 ())
+ 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);
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)
Spanner *spanner_me = dynamic_cast<Spanner*> (me);
Real ret = 0;
- if (spanner_me)
+ 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 ();
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)));
}
"Y-common "
"axes "
"elements "
+ "keep-fixed-while-stretching "
"max-stretch "
"pure-Y-common "
"pure-relevant-elements "
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)
{
"before-line-breaking "
"cause "
"color "
+ "cross-staff "
"extra-X-extent "
"extra-Y-extent "
"extra-offset "
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&,
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 *);
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));
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);
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
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. "
\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"
\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
(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
(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)
- (ly:grob-property sys 'stretchable)))
+ (> (max-stretch sys) 0.0)))
(define (height-estimate sys)
(interval-length
(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)