--- /dev/null
+\version "2.17.10"
+
+\header {
+ texidoc = "@code{add-stem-support} can be removed or implemented
+only for beamed notes.
+"
+}
+
+music = {
+ \clef bass
+ \stemUp
+ <g^3 a^5>2..->
+ r16 eeses'16
+ \set fingeringOrientations = #'(right)
+ <c e>8-1-4 <c^1 e^4> <g,-3 b,-4> r
+ r2
+}
+
+{
+ \music
+ \override Fingering #'add-stem-support = ##f
+ \music
+ \override Fingering #'add-stem-support = #only-if-beamed
+ \music
+}
\ No newline at end of file
-\version "2.16.0"
+\version "2.17.10"
\header {
- texidoc = "LilyPond automatically shifts dynamics that collide with
-cross-staff stems when manual beams are used."
+ texidoc = "Dynamics are correctly nested over/under cross staff stems.
+They are, however, not yet factored into horizontal spacing - the fff
+collides with other grobs.
+"
}
\new GrandStaff <<
\new Staff = "PnRH" {
\relative g {
\stemDown gis8 \p [ \change Staff = "PnLH" \stemUp a, \fff ]
+ a8 \p [ \change Staff = "PnRH" \stemDown gis'8 \fff ]
\change Staff = "PnRH" r4
}
}
- \new Staff = "PnLH" { \clef "F" { s4 r4 } }
+ \new Staff = "PnLH" { \clef "F" { s2 r4 } }
>>
-\version "2.17.6"
+\version "2.17.10"
\header {
texidoc = "It is possible to associate
fingerings uniquely with notes. This makes it possible to add
-horizontal fingerings to notes. Fingering clears stems and flags
-if @code{'add-stem-support} is set.
+horizontal fingerings to notes. Fingering defaults to not clearing
+flags and stems unless there is a collision or a beam.
"
}
-\version "2.17.6"
+\version "2.17.10"
\header {
texidoc = "Horizontal @code{Fingering} grobs that collide do not intersect.
-Non-intersecting @code{Fingering} grobs are left alone.
+Non-intersecting @code{Fingering} grobs are left alone. This is managed
+by the @code{FingeringColumn} grob.
"
}
-\version "2.17.6"
+\version "2.17.10"
\header {
composer = "ARTHUR GRAY"
http://www.orphee.com/comparison/study.html, see
http://www.orphee.com/comparison/gray.pdf
-Lines that contain tweaks (10 currently, not counting reverts) are
+Lines that contain tweaks (3 currently, not counting reverts) are
marked with %tweak
possibly more impressive to render without tweaks?
<e, gis, e d!>2
| %5
s8 cis4. d4
- % fair to count as one tweak?
- \override Fingering.add-stem-support = ##t %tweak
- \override Fingering.padding = #0.15
- \override Fingering.slur-padding = #0.1
<cis e,>8[( <d,_3 b'_1>
| %6
<cis_1 a'_2>)] cis'4. d4
return default_outside_staff_padding_;
}
+MAKE_SCHEME_CALLBACK (Axis_group_interface, cross_staff, 1);
+SCM
+Axis_group_interface::cross_staff (SCM smob)
+{
+ Grob *me = unsmob_grob (smob);
+ extract_grob_set (me, "elements", elts);
+ for (vsize i = 0; i < elts.size (); i++)
+ if (to_boolean (elts[i]->get_property ("cross-staff")))
+ return SCM_BOOL_T;
+
+ return SCM_BOOL_F;
+}
+
void
Axis_group_interface::add_element (Grob *me, Grob *e)
{
for (vsize i = 0; i < elts.size (); i++)
{
Grob *se = elts[i];
- if (!to_boolean (se->get_property ("cross-staff")))
+ if (has_interface (se)
+ || !to_boolean (se->get_property ("cross-staff")))
{
Interval dims = (bound && has_interface (se)
? generic_bound_extent (se, common, a)
{
Grob *g = elts[i];
- if (to_boolean (g->get_property ("cross-staff")))
+ if (to_boolean (g->get_property ("cross-staff"))
+ && !has_interface (g))
continue;
bool outside_staff = scm_is_number (g->get_property ("outside-staff-priority"));
Axis_group_interface::calc_skylines (SCM smob)
{
Grob *me = unsmob_grob (smob);
- extract_grob_set (me, Grob_array::unsmob (me->get_object ("vertical-skyline-elements")) ? "vertical-skyline-elements" : "elements", elts);
- Skyline_pair skylines = skyline_spacing (me, elts);
-
+ Skyline_pair skylines = skyline_spacing (me);
return skylines.smobbed_copy ();
}
SCM
Axis_group_interface::generic_group_extent (Grob *me, Axis a)
{
+ extract_grob_set (me, "elements", elts);
+
/* trigger the callback to do skyline-spacing on the children */
if (a == Y_AXIS)
- (void) me->get_property ("vertical-skylines");
+ for (vsize i = 0; i < elts.size (); i++)
+ (void) elts[i]->get_property ("vertical-skylines");
- extract_grob_set (me, "elements", elts);
Grob *common = common_refpoint_of_array (elts, me, a);
Real my_coord = me->relative_coordinate (common, a);
// that there is no room for the cross-staff grob. It also means, of course, that
// we don't get the benefits of skyline placement for cross-staff grobs.
Skyline_pair
-Axis_group_interface::skyline_spacing (Grob *me, vector<Grob *> elements)
+Axis_group_interface::skyline_spacing (Grob *me)
{
+ extract_grob_set (me, Grob_array::unsmob (me->get_object ("vertical-skyline-elements")) ? "vertical-skyline-elements" : "elements", fakeelements);
+ vector<Grob *> elements (fakeelements);
for (vsize i = 0; i < elements.size (); i++)
/*
As a sanity check, we make sure that no grob with an outside staff priority
vsize i = 0;
vector<Skyline_pair> inside_staff_skylines;
+
for (i = 0; i < elements.size ()
&& !scm_is_number (elements[i]->get_property ("outside-staff-priority")); i++)
{
Grob *elt = elements[i];
Grob *ancestor = outside_staff_ancestor (elt);
- if (!(to_boolean (elt->get_property ("cross-staff")) || ancestor))
+ if (!ancestor)
add_interior_skylines (elt, x_common, y_common, &inside_staff_skylines);
if (ancestor)
riders.insert (pair<Grob *, Grob *> (ancestor, elt));
--- /dev/null
+/*
+ This file is part of LilyPond, the GNU music typesetter.
+
+ Copyright (C) 2011--2012 Mike Solomon <mike@mikesolomon.org>
+ Jan Nieuwenhuizen <janneke@gnu.org>
+
+ 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 "box-quarantine.hh"
+#include <functional>
+
+Box_quarantine::Box_quarantine (Real padding, Axis a)
+ : padding_ (padding), a_ (a)
+{
+}
+
+void
+Box_quarantine::add_box_to_quarantine (Box infected)
+{
+ Box nbox (infected);
+ nbox[a_].widen (padding_ / 2);
+ boxes_to_quarantine_.push_back (nbox);
+}
+
+vector<Box>
+Box_quarantine::quarantined_boxes ()
+{
+ return boxes_to_quarantine_;
+}
+
+struct Intersection_info
+{
+ vsize index_;
+ Real amount_;
+ int mid_;
+ Intersection_info (vsize index, Real amount, int mid);
+};
+
+Intersection_info::Intersection_info (vsize index, Real amount, int mid)
+ : index_ (index), amount_ (amount), mid_ (mid)
+{
+}
+
+/*
+ boxes_to_quarantine_ contains a vector of boxes that may
+ or may not overlap. it iterates through these boxes,
+ pushing quarantined_boxes_ epsilon over or epsilon under a
+ collision. when this type of change happens, the loop is marked
+ as "dirty" and re-iterated.
+
+ TODO: not sure if this loop causes infinite behavior...
+*/
+
+bool
+sort_towards_middle(Intersection_info ii0, Intersection_info ii1)
+{
+ // ugh...with C++11, we'll just be able to do a factory instead of
+ // bundling mid with the Intersection_info
+ int mid = ii0.mid_;
+ assert ((double)(ii0.index_ - mid) >= 0.0);
+ assert ((double)(ii1.index_ - mid) >= 0.0);
+ return fabs (ii0.index_ - mid) < fabs (ii1.index_ - mid);
+}
+
+void
+Box_quarantine::solve ()
+{
+ int mid = boxes_to_quarantine_.size () / 2;
+ Real epsilon = 1.0e-4;
+ bool dirty = false;
+ do
+ {
+ dirty = false;
+ vector<Intersection_info> ii;
+ for (vsize i = 0; i < boxes_to_quarantine_.size () - 1; i++)
+ {
+ Box scratch (boxes_to_quarantine_[i]);
+ scratch.intersect (boxes_to_quarantine_[i + 1]);
+ if (!scratch.is_empty ())
+ ii.push_back (Intersection_info (i, scratch[a_].length (), mid));
+ }
+ vector_sort (ii, sort_towards_middle);
+ if (ii.size () > 0)
+ {
+ Offset shift (-(epsilon + ii[0].amount_ / 2.0), 0.0);
+ if (a_ == Y_AXIS)
+ shift = shift.swapped ();
+ boxes_to_quarantine_[ii[0].index_].translate (shift);
+ shift[a_] = -shift[a_];
+ boxes_to_quarantine_[ii[0].index_ + 1].translate (shift);
+ dirty = true;
+ }
+ }
+ while (dirty);
+}
interval_a_[Y_AXIS].set_empty ();
}
+bool
+Box::is_empty () const
+{
+ return interval_a_[X_AXIS].is_empty ()
+ || interval_a_[Y_AXIS].is_empty ();
+}
+
Box::Box (Interval ix, Interval iy)
{
x () = ix;
interval_a_[Y_AXIS].widen (y);
}
+void
+Box::intersect (Box b)
+{
+ interval_a_[X_AXIS].intersect (b[X_AXIS]);
+ interval_a_[Y_AXIS].intersect (b[Y_AXIS]);
+}
+
// for debugging
void
if (!e->get_parent (X_AXIS)
&& Side_position_interface::get_axis (e) == Y_AXIS)
e->set_parent (inf.grob (), X_AXIS);
+
+ Side_position_interface::add_support (e, inf.grob ());
}
}
class Dynamic_align_engraver : public Engraver
{
TRANSLATOR_DECLARATIONS (Dynamic_align_engraver);
- DECLARE_ACKNOWLEDGER (note_column);
+ DECLARE_ACKNOWLEDGER (rhythmic_head);
+ DECLARE_ACKNOWLEDGER (stem);
DECLARE_ACKNOWLEDGER (dynamic);
DECLARE_ACKNOWLEDGER (footnote_spanner);
DECLARE_END_ACKNOWLEDGER (dynamic);
}
ADD_ACKNOWLEDGER (Dynamic_align_engraver, dynamic);
-ADD_ACKNOWLEDGER (Dynamic_align_engraver, note_column);
+ADD_ACKNOWLEDGER (Dynamic_align_engraver, rhythmic_head);
+ADD_ACKNOWLEDGER (Dynamic_align_engraver, stem);
ADD_ACKNOWLEDGER (Dynamic_align_engraver, footnote_spanner);
ADD_END_ACKNOWLEDGER (Dynamic_align_engraver, dynamic);
}
void
-Dynamic_align_engraver::acknowledge_note_column (Grob_info info)
+Dynamic_align_engraver::acknowledge_rhythmic_head (Grob_info info)
+{
+ support_.push_back (info.grob ());
+}
+
+void
+Dynamic_align_engraver::acknowledge_stem (Grob_info info)
{
support_.push_back (info.grob ());
}
/**
Find potentially colliding scripts, and put them in a
- Fingering_column, that will fix the collisions. */
+ Fingering_column, that will fix the columns. */
class Fingering_column_engraver : public Engraver
{
Drul_array<Grob *> fingering_columns_;
#include "grob.hh"
#include "fingering-column.hh"
+#include "box-quarantine.hh"
#include "pointer-group-interface.hh"
#include "staff-symbol-referencer.hh"
#include "item.hh"
#include <map>
+// The sorting algorithm may not preserve the order of the
+// original fingerings. We use Fingering_position_info to retain
+// this order.
+
+struct Fingering_position_info {
+ Box box_;
+ vsize idx_;
+ Fingering_position_info (Box, vsize);
+};
+
+Fingering_position_info::Fingering_position_info (Box box, vsize idx)
+ : box_ (box), idx_ (idx)
+{
+}
+
+bool
+fingering_position_less (Fingering_position_info fpi0, Fingering_position_info fpi1)
+{
+ return Interval::left_less (fpi0.box_[Y_AXIS], fpi1.box_[Y_AXIS]);
+}
+
MAKE_SCHEME_CALLBACK (Fingering_column, calc_positioning_done, 1);
SCM
Fingering_column::calc_positioning_done (SCM smob)
{
Grob *me = unsmob_grob (smob);
- Real padding = robust_scm2double (me->get_property ("padding"), 0.0);
if (!me->is_live ())
return SCM_BOOL_T;
map<Grob *, bool> shifted;
- Real ss = Staff_symbol_referencer::staff_space (me);
-
me->set_property ("positioning-done", SCM_BOOL_T);
extract_grob_set (me, "fingerings", const_fingerings);
return SCM_BOOL_T;
}
- // order the fingerings from bottom to top
vector<Grob *> fingerings;
for (vsize i = 0; i < const_fingerings.size (); i++)
fingerings.push_back (const_fingerings[i]);
- vector_sort (fingerings, pure_position_less);
Grob *common[2] = {common_refpoint_of_array (fingerings, me, X_AXIS),
common_refpoint_of_array (fingerings, me, Y_AXIS)};
+ Real padding = robust_scm2double (me->get_property ("padding"), 0.2);
+ Box_quarantine bq (padding, Y_AXIS);
+ vector<Fingering_position_info> origs;
for (vsize i = 0; i < fingerings.size (); i++)
- fingerings[i]->translate_axis (-fingerings[i]->extent (common[Y_AXIS], Y_AXIS).length () / 2, Y_AXIS);
-
- for (vsize i = min (fingerings.size () - 1, fingerings.size () / 2 + 1); i >= 1; i--)
- for (vsize j = i; j--;)
- {
- Interval ex_i = fingerings[i]->extent (common[X_AXIS], X_AXIS);
- Interval ex_j = fingerings[j]->extent (common[X_AXIS], X_AXIS);
- Interval ey_i = fingerings[i]->extent (common[Y_AXIS], Y_AXIS);
- Interval ey_j = fingerings[j]->extent (common[Y_AXIS], Y_AXIS);
- Real tval = min (0.0, (ey_i[DOWN] - ey_j[UP] - padding) / 2);
- if (tval != 0.0 && !intersection (ex_i, ex_j).is_empty ())
- {
- if (shifted[fingerings[i]] || shifted[fingerings[j]])
- fingerings[j]->translate_axis (tval * 2, Y_AXIS);
- else
- {
- fingerings[i]->translate_axis (-tval, Y_AXIS);
- fingerings[j]->translate_axis (tval, Y_AXIS);
- }
- shifted[fingerings[i]] = true;
- shifted[fingerings[j]] = true;
- }
- }
-
- for (vsize i = fingerings.size () / 2 - 1; i < fingerings.size () - 1; i++)
- for (vsize j = i + 1; j < fingerings.size (); j++)
- {
- Interval ex_i = fingerings[i]->extent (common[X_AXIS], X_AXIS);
- Interval ex_j = fingerings[j]->extent (common[X_AXIS], X_AXIS);
- Interval ey_i = fingerings[i]->extent (common[Y_AXIS], Y_AXIS);
- Interval ey_j = fingerings[j]->extent (common[Y_AXIS], Y_AXIS);
- Real tval = max (0.0, (ey_i[UP] - ey_j[DOWN] + padding) / 2);
- if (tval != 0.0 && !intersection (ex_i, ex_j).is_empty ())
- {
- if (shifted[fingerings[i]] || shifted[fingerings[j]])
- fingerings[j]->translate_axis (tval * 2, Y_AXIS);
- else
- {
- fingerings[i]->translate_axis (-tval, Y_AXIS);
- fingerings[j]->translate_axis (tval, Y_AXIS);
- }
- shifted[fingerings[i]] = true;
- shifted[fingerings[j]] = true;
- }
- }
+ {
+ Interval x_ext = fingerings[i]->extent (common[X_AXIS], X_AXIS);
+ // center on Y parent
+ fingerings[i]->translate_axis (-fingerings[i]->extent (fingerings[i], Y_AXIS).length () / 2.0, Y_AXIS);
+ Interval y_ext = fingerings[i]->extent (fingerings[i], Y_AXIS)
+ + fingerings[i]->get_parent (Y_AXIS)
+ ->relative_coordinate (common[Y_AXIS], Y_AXIS);
+ origs.push_back(Fingering_position_info (Box (x_ext, y_ext), i));
+ }
+
+ // order the fingerings from bottom to top
+ vector_sort (origs, fingering_position_less);
+
+ for (vsize i = 0; i < origs.size (); i++)
+ bq.add_box_to_quarantine (Box (origs[i].box_));
+
+ bq.solve ();
+ vector<Box> news (bq.quarantined_boxes ());
+ for (vsize i = 0; i < origs.size (); i++)
+ fingerings[origs[i].idx_]->translate_axis (news[i][Y_AXIS][DOWN] - origs[i].box_[Y_AXIS][DOWN], Y_AXIS);
return SCM_BOOL_T;
}
DECLARE_TRANSLATOR_LISTENER (fingering);
DECLARE_ACKNOWLEDGER (rhythmic_head);
DECLARE_ACKNOWLEDGER (stem);
+ DECLARE_ACKNOWLEDGER (flag);
private:
void make_script (Direction, Stream_event *, int);
Side_position_interface::add_support (fingerings_[i], inf.grob ());
}
+void
+Fingering_engraver::acknowledge_flag (Grob_info inf)
+{
+ for (vsize i = 0; i < fingerings_.size (); i++)
+ Side_position_interface::add_support (fingerings_[i], inf.grob ());
+}
+
void
Fingering_engraver::acknowledge_rhythmic_head (Grob_info inf)
{
ADD_ACKNOWLEDGER (Fingering_engraver, rhythmic_head);
ADD_ACKNOWLEDGER (Fingering_engraver, stem);
+ADD_ACKNOWLEDGER (Fingering_engraver, flag);
ADD_TRANSLATOR (Fingering_engraver,
/* doc */
{
programming_error (to_string ("cyclic dependency: calculation-in-progress encountered for #'%s (%s)",
ly_symbol2string (sym).c_str (),
- name ().c_str ()));
+ name ().c_str ()));//assert (1==0);
if (debug_property_callbacks)
{
message ("backtrace: ");
if (get_property_data ("Y-extent") == SCM_EOL)
set_property ("Y-extent", Grob::stencil_height_proc);
if (get_property_data ("vertical-skylines") == SCM_EOL)
- set_property ("vertical-skylines", Grob::simple_vertical_skylines_from_stencil_proc);
+ set_property ("vertical-skylines", Grob::simple_vertical_skylines_from_extents_proc);
if (get_property_data ("horizontal-skylines") == SCM_EOL)
- set_property ("horizontal-skylines", Grob::simple_horizontal_skylines_from_stencil_proc);
+ set_property ("horizontal-skylines", Grob::simple_horizontal_skylines_from_extents_proc);
}
Grob::Grob (Grob const &s)
Grob::pure_height (Grob *refp, int start, int end)
{
SCM iv_scm = get_pure_property ("Y-extent", start, end);
+ // TODO: Why is this Interval (0,0)
+ // Shouldn't it just be an empty interval?
+ // 0,0 puts an arbitrary point at 0,0 which will influence spacing
Interval iv = robust_scm2interval (iv_scm, Interval (0, 0));
Real offset = pure_relative_y_coordinate (refp, start, end);
static Real get_default_outside_staff_padding ();
static Interval generic_bound_extent (Grob *me, Grob *common, Axis a);
static Interval pure_group_height (Grob *me, int start, int end);
+ DECLARE_SCHEME_CALLBACK (cross_staff, (SCM smob));
DECLARE_SCHEME_CALLBACK (width, (SCM smob));
DECLARE_SCHEME_CALLBACK (calc_x_common, (SCM smob));
DECLARE_SCHEME_CALLBACK (calc_y_common, (SCM smob));
static Interval part_of_line_pure_height (Grob *me, bool begin, int, int);
static Grob *outside_staff_ancestor (Grob *me);
- static Skyline_pair skyline_spacing (Grob *me, vector<Grob *> elements);
+ static Skyline_pair skyline_spacing (Grob *me);
static void add_element (Grob *me, Grob *);
static void set_axes (Grob *, Axis, Axis);
static bool has_axis (Grob *, Axis);
--- /dev/null
+/*
+ This file is part of LilyPond, the GNU music typesetter.
+
+ Copyright (C) 2012 Mike Solomon <mike@mikesolomon.org>
+
+ 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/>.
+*/
+
+#ifndef BOX_QUARANTINE_HH
+#define BOX_QUARANTINE_HH
+
+#include "lily-proto.hh"
+#include "std-vector.hh"
+#include "box.hh"
+
+class Box_quarantine
+{
+public:
+ Box_quarantine (Real, Axis);
+ void add_box_to_quarantine (Box);
+ void solve ();
+ vector<Box> quarantined_boxes ();
+
+private:
+ vector<Box> boxes_to_quarantine_;
+ Real padding_;
+ Axis a_;
+};
+
+#endif // BOX_QUARANTINE_HH
Interval operator [] (Axis a) const;
Interval &operator [] (Axis a);
Real area () const;
+ bool is_empty () const;
Offset center () const;
void widen (Real x, Real y);
void scale (Real r);
void unite (Box b);
+ void intersect (Box b);
void print ();
Box ();
Box (Interval ix, Interval iy);
DECLARE_SCHEME_CALLBACK (y_parent_positioning, (SCM));
DECLARE_SCHEME_CALLBACK (stencil_height, (SCM smob));
DECLARE_SCHEME_CALLBACK (stencil_width, (SCM smob));
- DECLARE_SCHEME_CALLBACK (simple_vertical_skylines_from_stencil, (SCM smob));
+ DECLARE_SCHEME_CALLBACK (pure_simple_vertical_skylines_from_extents, (SCM smob, SCM, SCM));
+ DECLARE_SCHEME_CALLBACK (simple_vertical_skylines_from_extents, (SCM smob));
DECLARE_SCHEME_CALLBACK (vertical_skylines_from_stencil, (SCM smob));
DECLARE_SCHEME_CALLBACK (vertical_skylines_from_element_stencils, (SCM smob));
- DECLARE_SCHEME_CALLBACK (simple_horizontal_skylines_from_stencil, (SCM smob));
+ DECLARE_SCHEME_CALLBACK (pure_simple_horizontal_skylines_from_extents, (SCM smob, SCM, SCM));
+ DECLARE_SCHEME_CALLBACK (simple_horizontal_skylines_from_extents, (SCM smob));
DECLARE_SCHEME_CALLBACK (horizontal_skylines_from_stencil, (SCM smob));
DECLARE_SCHEME_CALLBACK (horizontal_skylines_from_element_stencils, (SCM smob));
virtual bool pure_is_visible (int start, int end) const;
bool check_cross_staff (Grob *common);
static bool less (Grob *g1, Grob *g2);
- static SCM internal_simple_skylines_from_stencil (SCM, Axis);
+ static SCM maybe_pure_internal_simple_skylines_from_extents (Grob *, Axis, bool, int, int, bool, bool);
static SCM internal_skylines_from_element_stencils (SCM, Axis);
};
public:
DECLARE_GROB_INTERFACE ();
DECLARE_SCHEME_CALLBACK (print, (SCM));
+ DECLARE_SCHEME_CALLBACK (height, (SCM));
DECLARE_SCHEME_CALLBACK (percent, (SCM));
static void add_column (Grob *, Item *);
DECLARE_SCHEME_CALLBACK (set_spacing_rods, (SCM));
static SCM aligned_on_self (Grob *me, Axis a, bool pure, int start, int end);
static SCM centered_on_object (Grob *me, Axis a);
static SCM aligned_on_parent (Grob *me, Axis a);
- static SCM avoid_colliding_grobs (Grob *me, Axis a, Real offset);
static void set_center_parent (Grob *me, Axis a);
static void set_align_self (Grob *me, Axis a);
- static void avoid_x_collisions (Grob *me);
DECLARE_SCHEME_CALLBACK (x_aligned_on_self, (SCM element));
DECLARE_SCHEME_CALLBACK (y_aligned_on_self, (SCM element));
DECLARE_SCHEME_CALLBACK (centered_on_x_parent, (SCM element));
DECLARE_SCHEME_CALLBACK (centered_on_y_parent, (SCM element));
DECLARE_SCHEME_CALLBACK (x_centered_on_y_parent, (SCM element));
- DECLARE_SCHEME_CALLBACK (avoid_x_colliding_grobs, (SCM element, SCM offset));
- DECLARE_SCHEME_CALLBACK (x_colliding_grobs, (SCM element));
DECLARE_SCHEME_CALLBACK (aligned_on_x_parent, (SCM element));
DECLARE_SCHEME_CALLBACK (aligned_on_y_parent, (SCM element));
};
static SCM aligned_side (Grob *me, Axis a, bool pure, int start, int end, Real *current_off_ptr);
- static SCM general_side_position (Grob *, Axis, bool, bool my_extents,
- bool pure, int start, int end, Real *current_off);
- static SCM skyline_side_position (Grob *me, Axis a, bool pure, int start, int end, Real *current_offset);
-
static Axis get_axis (Grob *);
static void set_axis (Grob *, Axis);
DECLARE_GROB_INTERFACE ();
static void add_support (Grob *, Grob *);
+ static void recursive_add_support (Grob *, Grob *);
static void add_staff_support (Grob *);
};
Real max_height_position () const;
Real left () const;
Real right () const;
+ Direction direction () const;
void set_minimum_height (Real height);
void clear ();
bool is_empty () const;
*/
#include "interval-minefield.hh"
-#include "grob.hh"
+
Interval_minefield::Interval_minefield (Interval feasible_placements, Real bulk)
{
feasible_placements_ = feasible_placements;
return mol.smobbed_copy ();
}
+MAKE_SCHEME_CALLBACK (Multi_measure_rest, height, 1);
+SCM
+Multi_measure_rest::height (SCM smob)
+{
+ Grob *me = unsmob_grob (smob);
+ Spanner *sp = dynamic_cast<Spanner *> (me);
+
+ Real space = 1000000; // something very large...
+
+ Stencil mol;
+ mol.add_stencil (symbol_stencil (me, space));
+
+ return ly_interval2scm (mol.extent (Y_AXIS));
+}
+
int
calc_closest_duration_log (Grob *me, double duration, bool force_round_up, bool paranoid)
{
{
script_->set_parent (x_parent, X_AXIS);
Self_alignment_interface::set_center_parent (script_, X_AXIS);
- Self_alignment_interface::avoid_x_collisions (script_);
}
if (stem)
Pointer_group_interface::add_grob (script_, ly_symbol2scm ("potential-X-colliding-grobs"), stem);
vector<Finger_tuple> *scripts)
{
for (vsize i = 0; i < scripts->size (); i++)
- if (stem_ && to_boolean (scripts->at (i).script_->get_property ("add-stem-support")))
+ if (stem_)
{
Side_position_interface::add_support (scripts->at (i).script_, stem_);
if (Grob *flag = unsmob_grob (stem_->get_object ("flag")))
return 0;
}
+// If the grob array is unordered, we assume that duplicates should
+// be removed. This makes sense for things like side-position-elements,
+// which may be added recursively numerous times and thus will eat up
+// computation time when skylines are calculated.
+// If the array is ordered, then we don't remove duplicates.
+
void
Pointer_group_interface::add_grob (Grob *me, SCM sym, Grob *p)
{
Grob_array *arr = get_grob_array (me, sym);
arr->add (p);
+ if (!arr->ordered ())
+ arr->remove_duplicates ();
}
void
Grob_array *arr = get_grob_array (me, sym);
arr->add (p);
arr->set_ordered (false);
+ arr->remove_duplicates ();
}
static vector<Grob *> empty_array;
use it as a support for the current grob
*/
if (!scm_is_number (last_outside_staff))
- Side_position_interface::add_support (g, last);
+ Side_position_interface::recursive_add_support (g, last);
/*
if outside_staff_priority is missing or is equal to original
outside_staff_priority of previous grob, set new
return scm_from_double (x);
}
-MAKE_SCHEME_CALLBACK (Self_alignment_interface, avoid_x_colliding_grobs, 2);
-SCM
-Self_alignment_interface::avoid_x_colliding_grobs (SCM smob, SCM o)
-{
- SCM avoided = avoid_colliding_grobs (unsmob_grob (smob), X_AXIS, robust_scm2double (o, 0.0));
- return scm_is_null (avoided) ? o : avoided;
-}
-
-MAKE_SCHEME_CALLBACK (Self_alignment_interface, x_colliding_grobs, 1);
-SCM
-Self_alignment_interface::x_colliding_grobs (SCM smob)
-{
- Grob *me = unsmob_grob (smob);
- extract_grob_set (me, "potential-X-colliding-grobs", pot);
- vector<Grob *> act;
- Direction d = get_grob_direction (me->get_parent (Y_AXIS));
- for (vsize i = 0; i < pot.size (); i++)
- if (d == get_grob_direction (pot[i])
- && to_boolean (pot[i]->get_property ("cross-staff")))
- act.push_back (pot[i]);
-
- SCM grobs_scm = Grob_array::make_array ();
- unsmob_grob_array (grobs_scm)->set_array (act);
-
- return grobs_scm;
-}
-
-SCM
-Self_alignment_interface::avoid_colliding_grobs (Grob *me, Axis a, Real offset)
-{
- extract_grob_set (me, a == X_AXIS ? "X-colliding-grobs" : "Y-colliding-grobs", colls);
- if (!colls.size ())
- return SCM_EOL;
- vector<Interval> ivs;
-
- Item *refp = dynamic_cast<Item *> (common_refpoint_of_array (colls, me, a));
- if (!refp)
- return SCM_EOL;
-
- Interval iv = me->extent (me, a) + offset;
- for (vsize i = 0; i < colls.size (); i++)
- {
- int my_vai = Grob::get_vertical_axis_group_index (colls[i]);
- Direction dir = get_grob_direction (colls[i]);
- // if coll is cross staff but extremal and pointing in the
- // direction of the extrema, we don't take it into consideration
- if (Grob *beam = unsmob_grob (colls[i]->get_object ("beam")))
- {
- Interval_t<int> vais;
- extract_grob_set (beam, "normal-stems", stems);
- for (vsize j = 0; j < stems.size (); j++)
- vais.add_point (Grob::get_vertical_axis_group_index (stems[j]));
- // ugh...up and down are different for VerticalAxisGroup order...
- if ((my_vai == vais[DOWN] && dir == UP)
- || (my_vai == vais[UP] && dir == DOWN))
- continue;
- }
- ivs.push_back (colls[i]->extent (refp, a));
- }
-
- Interval_minefield minefield (Interval (iv.center (), iv.center ()), iv.length ());
- for (vsize i = 0; i < ivs.size (); i++)
- minefield.add_forbidden_interval (ivs[i]);
- minefield.solve ();
- Interval pos = minefield.feasible_placements ();
-
- if (pos[LEFT] == pos[RIGHT])
- return SCM_EOL;
-
- Direction col_dir = ((abs (pos[LEFT] - iv.center ())
- + robust_scm2double (me->get_property ("collision-bias"), 0.0))
- > abs (pos[RIGHT] - iv.center ()))
- ? RIGHT
- : LEFT;
-
- return scm_from_double ((pos[col_dir] - (iv.length () / 2)
- + col_dir
- * robust_scm2double (me->get_property ("collision-padding"), 0.0)));
-}
-
void
Self_alignment_interface::set_center_parent (Grob *me, Axis a)
{
a);
}
-void
-Self_alignment_interface::avoid_x_collisions (Grob *me)
-{
- chain_offset_callback (me, avoid_x_colliding_grobs_proc, X_AXIS);
-}
-
void
Self_alignment_interface::set_align_self (Grob *me, Axis a)
{
#include "directional-element-interface.hh"
#include "grob.hh"
#include "grob-array.hh"
+#include "international.hh"
+#include "item.hh"
#include "main.hh"
#include "misc.hh"
#include "note-head.hh"
Pointer_group_interface::add_unordered_grob (me, ly_symbol2scm ("side-support-elements"), e);
}
-SCM
-finish_offset (Grob *me, Direction dir, Real total_off, Real *current_offset)
+void
+Side_position_interface::recursive_add_support (Grob *me, Grob *e)
{
- Real ss = Staff_symbol_referencer::staff_space (me);
- Real minimum_space = ss * robust_scm2double (me->get_property ("minimum-space"), -1);
- total_off += dir * ss * robust_scm2double (me->get_property ("padding"), 0);
-
- if (minimum_space >= 0
- && dir
- && total_off * dir < minimum_space)
- total_off = minimum_space * dir;
-
- if (current_offset)
- total_off = dir * max (dir * total_off,
- dir * (*current_offset));
-
- /* FIXME: 1000 should relate to paper size. */
- if (fabs (total_off) > 1000)
- {
- string msg
- = String_convert::form_string ("Improbable offset for grob %s: %f",
- me->name ().c_str (), total_off);
-
- programming_error (msg);
- if (strict_infinity_checking)
- scm_misc_error (__FUNCTION__, "Improbable offset.", SCM_EOL);
- }
-
- return scm_from_double (total_off);
+ Pointer_group_interface::add_unordered_grob (me, ly_symbol2scm ("side-support-elements"), e);
+ extract_grob_set (e, "side-support-elements", sse);
+ for (vsize i = 0; i < sse.size (); i++)
+ recursive_add_support (me, sse[i]);
}
set<Grob *>
return support;
}
-/* Put the element next to the support, optionally taking in
- account the extent of the support.
-
- Does not take into account the extent of ME.
+/*
+ Position next to support, taking into account my own dimensions and padding.
*/
SCM
-Side_position_interface::general_side_position (Grob *me, Axis a, bool use_extents,
- bool include_my_extent,
- bool pure, int start, int end,
- Real *current_offset)
+axis_aligned_side_helper (SCM smob, Axis a, bool pure, int start, int end, SCM current_off_scm)
{
- set<Grob *> support = get_support_set (me);
-
- Grob *common = common_refpoint_of_array (support, me->get_parent (a), a);
- Grob *staff_symbol = Staff_symbol_referencer::get_staff_symbol (me);
- bool include_staff
- = staff_symbol
- && a == Y_AXIS
- && scm_is_number (me->get_property ("staff-padding"))
- && !to_boolean (me->get_property ("quantize-position"));
-
- Interval dim;
- Interval staff_extents;
- if (include_staff)
+ Real r;
+ Real *current_off_ptr = 0;
+ if (scm_is_number (current_off_scm))
{
- common = staff_symbol->common_refpoint (common, Y_AXIS);
- staff_extents = staff_symbol->maybe_pure_extent (common, Y_AXIS, pure, start, end);
-
- if (include_staff)
- dim.unite (staff_extents);
+ r = scm_to_double (current_off_scm);
+ current_off_ptr = &r;
}
- Direction dir = get_grob_direction (me);
-
- set<Grob *>::iterator it;
-
- for (it = support.begin (); it != support.end (); it++)
- {
- Grob *e = *it;
+ Grob *me = unsmob_grob (smob);
+ // We will only ever want widths of spanners after line breaking
+ // so we can set pure to false
+ if (dynamic_cast<Spanner *> (me) && a == X_AXIS)
+ pure = false;
- // In the case of a stem, we will find a note head as well
- // ignoring the stem solves cyclic dependencies if the stem is
- // attached to a cross-staff beam.
- if (a == Y_AXIS
- && Stem::has_interface (e)
- && dir == - get_grob_direction (e))
- continue;
+ return Side_position_interface::aligned_side (me, a, pure, start, end, current_off_ptr);
+}
- if (e)
- {
- if (use_extents)
- dim.unite (e->maybe_pure_extent (common, a, pure, start, end));
- else
- {
- Real x = e->maybe_pure_coordinate (common, a, pure, start, end);
- dim.unite (Interval (x, x));
- }
- }
- }
+MAKE_SCHEME_CALLBACK_WITH_OPTARGS (Side_position_interface, x_aligned_side, 2, 1, "");
+SCM
+Side_position_interface::x_aligned_side (SCM smob, SCM current_off)
+{
+ // Because horizontal skylines need vertical heights, we'd trigger
+ // unpure calculations too soon if this were called before line breaking.
+ // So, we always use pure heights. Given that horizontal skylines are
+ // almost always used before line breaking anyway, this doesn't cause
+ // problems.
+ return axis_aligned_side_helper (smob, X_AXIS, true, 0, 0, current_off);
+}
- if (dim.is_empty ())
- dim = Interval (0, 0);
+MAKE_SCHEME_CALLBACK_WITH_OPTARGS (Side_position_interface, y_aligned_side, 2, 1, "");
+SCM
+Side_position_interface::y_aligned_side (SCM smob, SCM current_off)
+{
+ return axis_aligned_side_helper (smob, Y_AXIS, false, 0, 0, current_off);
+}
- Real off = me->get_parent (a)->maybe_pure_coordinate (common, a, pure, start, end);
+MAKE_SCHEME_CALLBACK_WITH_OPTARGS (Side_position_interface, pure_y_aligned_side, 4, 1, "");
+SCM
+Side_position_interface::pure_y_aligned_side (SCM smob, SCM start, SCM end, SCM cur_off)
+{
+ return axis_aligned_side_helper (smob, Y_AXIS, true,
+ scm_to_int (start),
+ scm_to_int (end),
+ cur_off);
+}
- Real total_off = dim.linear_combination (dir) - off;
- if (include_my_extent)
- {
- Interval iv = me->maybe_pure_extent (me, a, pure, start, end);
- if (!iv.is_empty ())
- {
- if (!dir)
- {
- programming_error ("direction unknown, but aligned-side wanted");
- dir = DOWN;
- }
- total_off += -iv[-dir];
- }
- }
+MAKE_SCHEME_CALLBACK (Side_position_interface, calc_cross_staff, 1)
+SCM
+Side_position_interface::calc_cross_staff (SCM smob)
+{
+ Grob *me = unsmob_grob (smob);
+ extract_grob_set (me, "side-support-elements", elts);
+// Commented out because of cross staff issues
+// Direction for cross staff stems depends on the spacing of staves,
+// which depends on the inclusion of cross-staff side position grobs,
+// which need the direction for positioning. So the get_grob_direction call
+// may lead to circular dependencies.
+// #if 0
+ Direction my_dir = get_grob_direction (me) ;
+
+ // if a cross-staff grob is pointing in a different direction than
+ // that of an aligning element, we assume that the alignment
+ // of said element will not be influenced the cross-staffitude
+ // of the grob and thus we do not mark the aligning element
+ // as cross-staff
+ for (vsize i = 0; i < elts.size (); i++)
+ if (to_boolean (elts[i]->get_property ("cross-staff"))
+ && my_dir == get_grob_direction (elts[i]))
+ return SCM_BOOL_T;
+//#endif
+#if 0
+ for (vsize i = 0; i < elts.size (); i++)
+ if (to_boolean (elts[i]->get_property ("cross-staff")))
+ return SCM_BOOL_T;
+#endif
+ Grob *myvag = Grob::get_vertical_axis_group (me);
+ for (vsize i = 0; i < elts.size (); i++)
+ if (myvag != Grob::get_vertical_axis_group (elts[i]))
+ return SCM_BOOL_T;
- return finish_offset (me, dir, total_off, current_offset);
+ return SCM_BOOL_F;
}
+// long function - each stage is clearly marked
+
SCM
-Side_position_interface::skyline_side_position (Grob *me, Axis a,
- bool pure, int start, int end,
- Real *current_offset)
-{
+Side_position_interface::aligned_side (Grob *me, Axis a, bool pure, int start, int end,
+ Real *current_off)
+{//printf (" %s\n", me->name ().c_str ());
+ Direction dir = get_grob_direction (me);
+
set<Grob *> support = get_support_set (me);
Grob *common[2];
for (Axis ax = X_AXIS; ax < NO_AXES; incr (ax))
- common[ax] = common_refpoint_of_array (support, ax == a ? me->get_parent (ax) : me, ax);
+ common[ax] = common_refpoint_of_array (support,
+ (ax == a
+ ? me->get_parent (ax)
+ : me),
+ ax);
Grob *staff_symbol = Staff_symbol_referencer::get_staff_symbol (me);
- Direction dir = get_grob_direction (me);
+
+ bool include_staff
+ = staff_symbol
+ && a == Y_AXIS
+ && scm_is_number (me->get_property ("staff-padding"))
+ && !to_boolean (me->get_property ("quantize-position"));
+
+ if (include_staff)
+ common[Y_AXIS] = staff_symbol->common_refpoint (common[Y_AXIS], Y_AXIS);
Skyline my_dim;
- Skyline_pair *sp = Skyline_pair::unsmob (me->get_property ("vertical-skylines"));
- if (sp && a == Y_AXIS && !pure)
+ Skyline_pair *skyp = Skyline_pair::unsmob (
+ me->get_maybe_pure_property (a == X_AXIS
+ ? "horizontal-skylines"
+ : "vertical-skylines",
+ pure,
+ start,
+ end));
+ if (skyp)
{
- Skyline_pair copy = Skyline_pair (*sp);
- copy.shift (me->relative_coordinate (common[X_AXIS], X_AXIS));
- copy.raise (me->get_parent (Y_AXIS)->relative_coordinate (common[Y_AXIS], Y_AXIS));
+ // for spanner pure heights, we don't know horizontal spacing,
+ // so a spanner can never have a meaningful x coordiante
+ // we just give it the parents' coordinate because its
+ // skyline will likely be of infinite width anyway
+ // and we don't want to prematurely trigger H spacing
+ Real xc = a == X_AXIS || (pure && dynamic_cast<Spanner *> (me))
+ ? me->get_parent (X_AXIS)->relative_coordinate (common[X_AXIS], X_AXIS)
+ : me->relative_coordinate (common[X_AXIS], X_AXIS);
+ // same here, for X_AXIS spacing, if it's happening, it should only be
+ // before line breaking. because there is no thing as "pure" x spacing,
+ // we assume that it is all pure
+ Real yc = a == X_AXIS
+ ? me->pure_relative_y_coordinate (common[Y_AXIS], start, end)
+ : me->get_parent (Y_AXIS)->maybe_pure_coordinate (common[Y_AXIS], Y_AXIS, pure, start, end);
+ Skyline_pair copy = Skyline_pair (*skyp);
+ copy.shift (a == X_AXIS ? yc : xc);
+ copy.raise (a == X_AXIS ? xc : yc);
my_dim = copy[-dir];
}
else
- {
- Box off;
- for (Axis ax = X_AXIS; ax < NO_AXES; incr (ax))
- {
- if (ax == a)
- off[ax] = me->get_parent (ax)->maybe_pure_coordinate (common[ax], ax, pure, start, end)
- + me->maybe_pure_extent (me, ax, pure, start, end);
- else
- off[ax] = me->maybe_pure_extent (common[ax], ax, pure, start, end);
- }
+ me->warning ("cannot find skylines - strange alignment will follow");
- if (off[X_AXIS].is_empty () || off[Y_AXIS].is_empty ())
- return scm_from_double (0.0);
-
- my_dim = Skyline (off, other_axis (a), -dir);
- }
- bool include_staff
- = staff_symbol
- && a == Y_AXIS
- && scm_is_number (me->get_property ("staff-padding"))
- && !to_boolean (me->get_property ("quantize-position"));
vector<Box> boxes;
vector<Skyline_pair> skyps;
- Real min_h = dir == LEFT ? infinity_f : -infinity_f;
set<Grob *>::iterator it;
+ Real max_raise = -dir * infinity_f;
+ bool aligns_to_cross_staff = false;
- map<Grob *, vector<Grob *> > note_column_map; // for parts of a note column
for (it = support.begin (); it != support.end (); it++)
{
Grob *e = *it;
-
+//printf (" %s\n", e->name ().c_str ());
// In the case of a stem, we will find a note head as well
// ignoring the stem solves cyclic dependencies if the stem is
// attached to a cross-staff beam.
if (e)
{
- if (Note_column::has_interface (e->get_parent (X_AXIS))
- && to_boolean (me->get_property ("add-stem-support")))
- {
- note_column_map[e->get_parent (X_AXIS)].push_back (e);
- continue;
- }
- Skyline_pair *sp = Skyline_pair::unsmob (e->get_property ("vertical-skylines"));
- if (sp && a == Y_AXIS && !pure)
+ bool cross_staff = to_boolean (e->get_property ("cross-staff"));
+
+ Skyline_pair *sp = Skyline_pair::unsmob
+ (e->get_maybe_pure_property (a == X_AXIS
+ ? "horizontal-skylines"
+ : "vertical-skylines",
+ pure || cross_staff,
+ start,
+ end));
+
+ aligns_to_cross_staff |= cross_staff;
+ if (sp)
{
+ Real xc = pure && dynamic_cast<Spanner *> (e)
+ ? e->get_parent (X_AXIS)->relative_coordinate (common[X_AXIS], X_AXIS)
+ : e->relative_coordinate (common[X_AXIS], X_AXIS);
+ // same logic as above
+ // we assume horizontal spacing is always pure
+ Real yc = a == X_AXIS
+ ? e->pure_relative_y_coordinate (common[Y_AXIS], start, end)
+ : e->maybe_pure_coordinate (common[Y_AXIS], Y_AXIS, pure, start, end);
Skyline_pair copy = Skyline_pair (*sp);
- copy.shift (e->relative_coordinate (common[X_AXIS], X_AXIS));
- copy.raise (e->relative_coordinate (common[Y_AXIS], Y_AXIS));
+ if (a == Y_AXIS
+ && Stem::has_interface (e)
+ && to_boolean (me->get_property ("add-stem-support")))
+ copy[dir].set_minimum_height (copy[dir].max_height ());
+ copy.shift (a == X_AXIS ? yc : xc);
+ copy.raise (a == X_AXIS ? xc : yc);
+ max_raise = minmax (dir, max_raise, a == X_AXIS ? xc : yc);
skyps.push_back (copy);
- continue;
}
- Box b;
- for (Axis ax = X_AXIS; ax < NO_AXES; incr (ax))
- b[ax] = e->maybe_pure_extent (common[ax], ax, pure, start, end);
-
- if (b[X_AXIS].is_empty () || b[Y_AXIS].is_empty ())
- continue;
-
- boxes.push_back (b);
- min_h = minmax (dir, b[a][-dir], min_h);
+ else { /* no warning*/ }
}
}
- // this loop ensures that parts of a note column will be in the same box
- // pushes scripts and such over stems instead of just over heads
- for (map<Grob *, vector<Grob *> >::iterator i = note_column_map.begin (); i != note_column_map.end (); i++)
- {
- Box big;
- for (vsize j = 0; j < (*i).second.size (); j++)
- {
- Grob *e = (*i).second[j];
- Box b;
- for (Axis ax = X_AXIS; ax < NO_AXES; incr (ax))
- b[ax] = e->maybe_pure_extent (common[ax], ax, pure, start, end);
-
- if (b[X_AXIS].is_empty () || b[Y_AXIS].is_empty ())
- continue;
-
- big.unite (b);
- }
- if (!big[X_AXIS].is_empty () && !big[Y_AXIS].is_empty ())
- boxes.push_back (big);
- }
-
Skyline dim (boxes, other_axis (a), dir);
if (skyps.size ())
{
Skyline_pair merged (skyps);
dim.merge (merged[dir]);
}
- if (!boxes.size ())
- dim.set_minimum_height (0.0);
- else
- dim.set_minimum_height (min_h);
if (include_staff)
{
Interval staff_extents;
common[Y_AXIS] = staff_symbol->common_refpoint (common[Y_AXIS], Y_AXIS);
staff_extents = staff_symbol->maybe_pure_extent (common[Y_AXIS], Y_AXIS, pure, start, end);
- dim.set_minimum_height (minmax (dir, min_h, staff_extents[dir]));
+ dim.set_minimum_height (staff_extents[dir]);
}
- Real dist = dim.distance (my_dim, 0.1); // 0.1 m4g1c value...fix...
- Real total_off = !isinf (dist) ? dir * dist : 0.0;
-
- return finish_offset (me, dir, total_off, current_offset);
-}
-
-MAKE_SCHEME_CALLBACK (Side_position_interface, y_aligned_on_support_refpoints, 1);
-SCM
-Side_position_interface::y_aligned_on_support_refpoints (SCM smob)
-{
- return general_side_position (unsmob_grob (smob), Y_AXIS, false, false, false, 0, 0, 0);
-}
-
-MAKE_SCHEME_CALLBACK (Side_position_interface, pure_y_aligned_on_support_refpoints, 3);
-SCM
-Side_position_interface::pure_y_aligned_on_support_refpoints (SCM smob, SCM start, SCM end)
-{
- return general_side_position (unsmob_grob (smob), Y_AXIS, false, false,
- true, scm_to_int (start), scm_to_int (end), 0);
-}
-
-/*
- Position next to support, taking into account my own dimensions and padding.
-*/
-SCM
-axis_aligned_side_helper (SCM smob, Axis a, bool pure, int start, int end, SCM current_off_scm)
-{
- Real r;
- Real *current_off_ptr = 0;
- if (scm_is_number (current_off_scm))
+ // this seems kinda kludgy, as there is no apparent logic to it
+ // however, it is a holdover from the previous code and
+ // necessary for the InstrumentName grob
+ // TODO: find a better way to deal with this...
+ if (dim.is_empty ())
{
- r = scm_to_double (current_off_scm);
- current_off_ptr = &r;
+ dim = Skyline (dim.direction ());
+ dim.set_minimum_height (0.0);
}
- return Side_position_interface::aligned_side (unsmob_grob (smob), a, pure, start, end, current_off_ptr);
-}
+ // Ditto - seems kludgy, but this time logic of SystemStartBrackets
+ if (my_dim.is_empty ())
+ {
+ my_dim = Skyline (my_dim.direction ());
+ my_dim.set_minimum_height (isinf (max_raise) ? 0.0 : max_raise);
+ }
-MAKE_SCHEME_CALLBACK_WITH_OPTARGS (Side_position_interface, x_aligned_side, 2, 1, "");
-SCM
-Side_position_interface::x_aligned_side (SCM smob, SCM current_off)
-{
- return axis_aligned_side_helper (smob, X_AXIS, false, 0, 0, current_off);
-}
+ // Many cross-staff grobs do not have good height estimations.
+ // We give the grob the best chance of not colliding by shifting
+ // it to the maximum height in the case of cross-staff alignment.
+ // This means, in other words, that the old way things were done
+ // (using boxes instead of skylines) is just reactivated for
+ // alignment to cross-staff grobs.
+ if (aligns_to_cross_staff)
+ dim.set_minimum_height (dim.max_height ());
-MAKE_SCHEME_CALLBACK_WITH_OPTARGS (Side_position_interface, y_aligned_side, 2, 1, "");
-SCM
-Side_position_interface::y_aligned_side (SCM smob, SCM current_off)
-{
- return axis_aligned_side_helper (smob, Y_AXIS, false, 0, 0, current_off);
-}
+ Real ss = Staff_symbol_referencer::staff_space (me);
+ Real dist = dim.distance (my_dim, robust_scm2double (me->get_property ("horizon-padding"), 0.0));
+ Real total_off = !isinf (dist) ? dir * dist : 0.0;
-MAKE_SCHEME_CALLBACK_WITH_OPTARGS (Side_position_interface, pure_y_aligned_side, 4, 1, "");
-SCM
-Side_position_interface::pure_y_aligned_side (SCM smob, SCM start, SCM end, SCM cur_off)
-{
- return axis_aligned_side_helper (smob, Y_AXIS, true,
- scm_to_int (start),
- scm_to_int (end),
- cur_off);
-}
+ total_off += dir * ss * robust_scm2double (me->get_property ("padding"), 0.0);
-MAKE_SCHEME_CALLBACK (Side_position_interface, calc_cross_staff, 1)
-SCM
-Side_position_interface::calc_cross_staff (SCM smob)
-{
- Grob *me = unsmob_grob (smob);
- extract_grob_set (me, "side-support-elements", elts);
+ Real minimum_space = ss * robust_scm2double (me->get_property ("minimum-space"), -1);
- for (vsize i = 0; i < elts.size (); i++)
- if (to_boolean (elts[i]->get_property ("cross-staff")))
- return SCM_BOOL_T;
+ if (minimum_space >= 0
+ && dir
+ && total_off * dir < minimum_space)
+ total_off = minimum_space * dir;
- Grob *common = common_refpoint_of_array (elts, me->get_parent (Y_AXIS), Y_AXIS);
- return scm_from_bool (common != me->get_parent (Y_AXIS));
-}
+ if (current_off)
+ total_off = dir * max (dir * total_off,
+ dir * (*current_off));
-SCM
-Side_position_interface::aligned_side (Grob *me, Axis a, bool pure, int start, int end,
- Real *current_off)
-{
- Direction dir = get_grob_direction (me);
- bool skyline = to_boolean (me->get_property ("use-skylines"));
+ /* FIXME: 1000 should relate to paper size. */
+ if (fabs (total_off) > 1000)
+ {
+ string msg
+ = String_convert::form_string ("Improbable offset for grob %s: %f",
+ me->name ().c_str (), total_off);
- Real o = scm_to_double (skyline && !pure
- ? skyline_side_position (me, a, pure, start, end, current_off)
- : general_side_position (me, a, true, true, pure, start, end, current_off));
+ programming_error (msg);
+ if (strict_infinity_checking)
+ scm_misc_error (__FUNCTION__, "Improbable offset.", SCM_EOL);
+ }
/*
Maintain a minimum distance to the staff. This is similar to side
Real my_off = me->get_parent (Y_AXIS)->maybe_pure_coordinate (common, Y_AXIS, pure, start, end);
Real staff_off = staff->maybe_pure_coordinate (common, Y_AXIS, pure, start, end);
Real ss = Staff_symbol::staff_space (staff);
- Real position = 2 * (my_off + o - staff_off) / ss;
+ Real position = 2 * (my_off + total_off - staff_off) / ss;
Real rounded = directed_round (position, dir);
Grob *head = me->get_parent (X_AXIS);
&& abs (Staff_symbol_referencer::get_position (head)) > abs (position)))
{
- o += (rounded - position) * 0.5 * ss;
+ total_off += (rounded - position) * 0.5 * ss;
if (Staff_symbol_referencer::on_line (me, int (rounded)))
- o += dir * 0.5 * ss;
+ total_off += dir * 0.5 * ss;
}
}
else if (scm_is_number (me->get_property ("staff-padding")) && dir)
Real staff_position = staff->maybe_pure_coordinate (common, Y_AXIS, pure, start, end);
Interval staff_extent = staff->maybe_pure_extent (staff, a, pure, start, end);
Real diff = (dir * staff_extent[dir] + staff_padding
- - dir * (o + iv[-dir])
+ - dir * (total_off + iv[-dir])
+ dir * (staff_position - parent_position));
- o += dir * max (diff, 0.0);
+ total_off += dir * max (diff, 0.0);
}
}
- return scm_from_double (o);
+ return scm_from_double (total_off);
}
void
"add-stem-support "
"direction "
"minimum-space "
+ "horizon-padding "
"padding "
"quantize-position "
"side-axis "
{
bool last_empty = false;
list<Building>::iterator i;
+
for (i = buildings_.begin (); i != buildings_.end (); i++)
{
if (last_empty && i->y_intercept_ == -infinity_f)
{
if (b.end_ > b.start_ + EPS)
{
- ret->push_back (Building (-infinity_f, -infinity_f,
- -infinity_f, b.start_));
+ if (b.start_ != -infinity_f)
+ ret->push_back (Building (-infinity_f, -infinity_f,
+ -infinity_f, b.start_));
ret->push_back (b);
- ret->push_back (Building (b.end_, -infinity_f,
- -infinity_f, infinity_f));
+ if (b.end_ != infinity_f)
+ ret->push_back (Building (b.end_, -infinity_f,
+ -infinity_f, infinity_f));
}
else
{
sky_ = sky;
Building front (b, horizon_axis, sky);
single_skyline (front, &buildings_);
+ normalize ();
}
void
return sky_ * ret;
}
+Direction
+Skyline::direction () const
+{
+ return sky_;
+}
+
Real
Skyline::left () const
{
#include "pointer-group-interface.hh"
#include "lily-guile.hh"
#include "real.hh"
+#include "rest.hh"
#include "stencil.hh"
#include "string-convert.hh"
#include "skyline.hh"
#include "skyline-pair.hh"
+#include "spanner.hh"
using namespace std;
Real QUANTIZATION_UNIT = 0.2;
do
{
Offset inter_l = get_point_in_y_direction (left, perpendicular_slope (slope), thick / 2, d);
- Offset inter_r = get_point_in_y_direction (right, perpendicular_slope (slope), thick / 2, d);//printf ("O %4.4f %4.4f\n", inter_l[X_AXIS], inter_r[X_AXIS]);printf ("TRANNY %4.4f %4.4f %4.4f %4.4f %4.4f %4.4f\n", trans.xx, trans.xy, trans.yx, trans.yy, trans.x0, trans.y0);
+ Offset inter_r = get_point_in_y_direction (right, perpendicular_slope (slope), thick / 2, d);
pango_matrix_transform_point (&trans, &inter_l[X_AXIS], &inter_l[Y_AXIS]);
pango_matrix_transform_point (&trans, &inter_r[X_AXIS], &inter_r[Y_AXIS]);
if ((inter_l[X_AXIS] == inter_r[X_AXIS]) || (inter_l[Y_AXIS] == inter_r[Y_AXIS]))
{
- //printf ("OO %4.4f %4.4f\n", inter_l[X_AXIS], inter_r[X_AXIS]);
Box b;
b.add_point (inter_l);
b.add_point (inter_r);
}
SCM
-Grob::internal_simple_skylines_from_stencil (SCM smob, Axis a)
+Grob::maybe_pure_internal_simple_skylines_from_extents (Grob *me, Axis a, bool pure, int beg, int end, bool ignore_x, bool ignore_y)
{
- Grob *me = unsmob_grob (smob);
-
- if (to_boolean (me->get_property ("cross-staff")))
+ vector<Box> boxes;
+ // we don't know how far spanners stretch along the X axis before
+ // line breaking. better have them take up the whole thing
+ Interval xex = ignore_x
+ ? Interval (-infinity_f, infinity_f)
+ : me->extent (me, X_AXIS);
+
+ // If we're looking at the x exent of a cross staff grob, it could be
+ // very early on in the computation process. We won't know its height
+ // until way later, so we give a brute force approximation.
+ Interval yex = ignore_y
+ ? Interval (-infinity_f, infinity_f)
+ : me->maybe_pure_extent (me, Y_AXIS, pure, beg, end);
+
+ // In horizontal spacing, there are grobs like SystemStartBracket
+ // that take up no vertical spcae. So, if the y extent is empty,
+ // we use the entire Y extent ot make the X a sort of horizontal wall.
+ // Ditto for vertical spacing and grobs like BassFigureAlginmentPositioning.
+ if (a == Y_AXIS && yex.is_empty ())
+ yex.set_full ();
+
+ if (a == X_AXIS && xex.is_empty ())
+ xex.set_full ();
+
+ if (xex.is_empty () || yex.is_empty ())
return Skyline_pair ().smobbed_copy ();
- extract_grob_set (me, "elements", elts);
- if (elts.size ())
- return internal_skylines_from_element_stencils (smob, a);
+ boxes.push_back (Box (xex, yex));
+ return Skyline_pair (boxes, a).smobbed_copy ();
+}
- Stencil *s = unsmob_stencil (me->get_property ("stencil"));
- if (!s)
- return Skyline_pair ().smobbed_copy ();
+MAKE_SCHEME_CALLBACK (Grob, pure_simple_vertical_skylines_from_extents, 3);
+SCM
+Grob::pure_simple_vertical_skylines_from_extents (SCM smob, SCM begscm, SCM endscm)
+{
+ Grob *me = unsmob_grob (smob);
+ int beg = robust_scm2int (begscm, 0);
+ int end = robust_scm2int (endscm, INT_MAX);
+ return maybe_pure_internal_simple_skylines_from_extents (me, X_AXIS, true, beg, end, dynamic_cast<Spanner *> (me), false);
+}
- vector<Box> boxes;
- boxes.push_back (Box (s->extent (X_AXIS), s->extent (Y_AXIS)));
- return Skyline_pair (boxes, a).smobbed_copy ();
+MAKE_SCHEME_CALLBACK (Grob, simple_vertical_skylines_from_extents, 1);
+SCM
+Grob::simple_vertical_skylines_from_extents (SCM smob)
+{
+ Grob *me = unsmob_grob (smob);
+ return maybe_pure_internal_simple_skylines_from_extents (me, X_AXIS, false, 0, 0, false, false);
}
-MAKE_SCHEME_CALLBACK (Grob, simple_vertical_skylines_from_stencil, 1);
+MAKE_SCHEME_CALLBACK (Grob, pure_simple_horizontal_skylines_from_extents, 3);
SCM
-Grob::simple_vertical_skylines_from_stencil (SCM smob)
+Grob::pure_simple_horizontal_skylines_from_extents (SCM smob, SCM begscm, SCM endscm)
{
- return internal_simple_skylines_from_stencil (smob, X_AXIS);
+ Grob *me = unsmob_grob (smob);
+ int beg = robust_scm2int (begscm, 0);
+ int end = robust_scm2int (endscm, INT_MAX);
+ return maybe_pure_internal_simple_skylines_from_extents (me, Y_AXIS, true, beg, end, false, to_boolean (me->get_property ("cross-staff")));
}
-MAKE_SCHEME_CALLBACK (Grob, simple_horizontal_skylines_from_stencil, 1);
+MAKE_SCHEME_CALLBACK (Grob, simple_horizontal_skylines_from_extents, 1);
SCM
-Grob::simple_horizontal_skylines_from_stencil (SCM smob)
+Grob::simple_horizontal_skylines_from_extents (SCM smob)
{
- return internal_simple_skylines_from_stencil (smob, Y_AXIS);
+ Grob *me = unsmob_grob (smob);
+ return maybe_pure_internal_simple_skylines_from_extents (me, Y_AXIS, false, 0, 0, false, to_boolean (me->get_property ("cross-staff")));
}
SCM
slur, the closer it is to this height.")
(hide-tied-accidental-after-break ,boolean? "If set, an accidental
that appears on a tied note after a line break will not be displayed.")
+ (horizon-padding ,number? "The amount to pad the axis
+along which a @code{Skyline} is built for the
+@code{side-position-interface}.")
(horizontal-shift ,integer? "An integer that identifies ranking
of @code{NoteColumn}s for horizontal shifting. This is used by
@rinternals{note-collision-interface}.")
(side-axis . ,Y)
(staff-padding . 0.25)
(stencil . ,ly:accidental-interface::print)
- (use-skylines . #t)
(X-extent . ,ly:accidental-interface::width)
(X-offset . ,(ly:make-simple-closure
`(,+
(font-family . roman)
(font-size . -2)
(non-musical . #t)
+ ;; w/o padding, bars numbers are not positioned over the staff as
+ ;; they are slightly to the left. so we add just a bit.
+ (horizon-padding . 0.05)
(outside-staff-priority . 100)
(padding . 1.0)
(self-alignment-X . ,RIGHT)
(next-note . (extra-space . 1.0))
(right-edge . (extra-space . 0.5))))
(stencil . ,ly:clef::print)
+ (vertical-skylines . ,ly:grob::vertical-skylines-from-stencil)
(Y-offset . ,ly:staff-symbol-referencer::callback)
(meta . ((class . Item)
(object-callbacks . ((pure-Y-common . ,ly:axis-group-interface::calc-pure-y-common)
(slur-padding . 0.3)
(staff-padding . 0.1)
(vertical-skylines . ,ly:grob::vertical-skylines-from-element-stencils)
- (use-skylines . #t)
(X-extent . ,ly:axis-group-interface::width)
(Y-extent . ,ly:axis-group-interface::height)
(Y-offset . ,ly:side-position-interface::y-aligned-side)
(X-offset . ,ly:self-alignment-interface::x-aligned-on-self)
(Y-offset . ,ly:self-alignment-interface::y-aligned-on-self)
(meta . ((class . Item)
- (object-callbacks . ((X-colliding-grobs . ,ly:self-alignment-interface::x-colliding-grobs)))
(interfaces . (dynamic-interface
dynamic-text-interface
font-interface
. (
;; sync with TextScript (?)
-
+ (add-stem-support . ,only-if-beamed)
(avoid-slur . around)
(cross-staff . ,script-or-side-position-cross-staff)
(direction . ,ly:script-interface::calc-direction)
(thick-thickness . 6.6)
;; See Wanske pp. 125
(usable-duration-logs . ,(iota 4 -3))
+ (Y-extent . ,ly:multi-measure-rest::height)
(Y-offset . ,ly:staff-symbol-referencer::callback)
(meta . ((class . Spanner)
(interfaces . (font-interface
,(ly:make-simple-closure
(list ly:self-alignment-interface::x-centered-on-y-parent)))))
(Y-offset . ,ly:side-position-interface::y-aligned-side)
+ (vertical-skylines . ,ly:grob::vertical-skylines-from-stencil)
(meta . ((class . Spanner)
(interfaces . (font-interface
multi-measure-interface
,(ly:make-simple-closure
(list ly:self-alignment-interface::x-aligned-on-self)))))
(Y-offset . ,ly:side-position-interface::y-aligned-side)
+ (vertical-skylines . ,ly:grob::vertical-skylines-from-stencil)
(meta . ((class . Spanner)
(interfaces . (font-interface
multi-measure-interface
. (
(axes . (,X ,Y))
(bound-alignment-interfaces . (rhythmic-head-interface stem-interface))
+ (cross-staff . ,ly:axis-group-interface::cross-staff)
(horizontal-skylines . ,ly:separation-item::calc-skylines)
(skyline-vertical-padding . 0.15)
(X-extent . ,ly:axis-group-interface::width)
,(ly:make-simple-closure
(list ly:self-alignment-interface::centered-on-x-parent)))))
(Y-offset . ,ly:side-position-interface::y-aligned-side)
+ (vertical-skylines . ,ly:grob::vertical-skylines-from-stencil)
(meta . ((class . Item)
(interfaces . (font-interface
octavate-eight-interface
(X-extent . ,ly:rest::width)
(Y-extent . ,ly:rest::height)
(Y-offset . ,ly:rest::y-offset-callback)
+ (vertical-skylines . ,ly:grob::vertical-skylines-from-stencil)
(meta . ((class . Item)
(interfaces . (font-interface
rest-interface
(staff-padding . 0.25)
(stencil . ,ly:script-interface::print)
- (use-skylines . #t)
(vertical-skylines . ,ly:grob::vertical-skylines-from-stencil)
(X-offset . ,script-interface::calc-x-offset)
(Y-offset . ,ly:side-position-interface::y-aligned-side)
(SostenutoPedalLineSpanner
. (
(axes . (,Y))
+ (cross-staff . ,ly:side-position-interface::calc-cross-staff)
(direction . ,DOWN)
(minimum-space . 1.0)
(outside-staff-priority . 1000)
(SustainPedalLineSpanner
. (
(axes . (,Y))
+ (cross-staff . ,ly:side-position-interface::calc-cross-staff)
(direction . ,DOWN)
(minimum-space . 1.0)
(outside-staff-priority . 1000)
(outside-staff-priority . 450)
;; sync with Fingering ?
- (padding . 0.5)
+ (padding . 0.3)
(script-priority . 200)
(side-axis . ,Y)
(UnaCordaPedalLineSpanner
. (
(axes . (,Y))
+ (cross-staff . ,ly:side-position-interface::calc-cross-staff)
(direction . ,DOWN)
(minimum-space . 1.0)
(outside-staff-priority . 1000)
(,ly:axis-group-interface::height . ,ly:axis-group-interface::pure-height)
(,ly:beam::rest-collision-callback . ,ly:beam::pure-rest-collision-callback)
(,ly:flag::calc-y-offset . ,ly:flag::pure-calc-y-offset)
+ (,ly:grob::horizontal-skylines-from-stencil . ,ly:grob::pure-simple-horizontal-skylines-from-extents)
+ (,ly:grob::horizontal-skylines-from-element-stencils . ,ly:grob::pure-simple-horizontal-skylines-from-extents)
+ (,ly:grob::simple-horizontal-skylines-from-extents . ,ly:grob::pure-simple-horizontal-skylines-from-extents)
+ (,ly:grob::simple-vertical-skylines-from-extents . ,ly:grob::pure-simple-vertical-skylines-from-extents)
(,ly:grob::stencil-height . ,pure-stencil-height)
+ (,ly:grob::vertical-skylines-from-stencil . ,ly:grob::pure-simple-vertical-skylines-from-extents)
+ (,ly:grob::vertical-skylines-from-element-stencils . ,ly:grob::pure-simple-vertical-skylines-from-extents)
(,ly:hara-kiri-group-spanner::y-extent . ,ly:hara-kiri-group-spanner::pure-height)
(,ly:rest-collision::force-shift-callback-rest . ,pure-chain-offset-callback)
(,ly:rest::height . ,ly:rest::pure-height)
(define pure-functions
(list
+ ly:accidental-interface::horizontal-skylines
parenthesize-elements
laissez-vibrer::print
+ ly:multi-measure-rest::height
ly:rest::y-offset-callback
ly:staff-symbol-referencer::callback
ly:staff-symbol::height))
(define-public (reverse-interval iv)
(cons (cdr iv) (car iv)))
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; boolean
+
+(define (lily-and a b)
+ (and a b))
+
+(define (lily-or a b)
+ (or a b))
+
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; coordinates
((ly:grob? cause) (event-cause cause))
(else #f))))
+(define-public (non-event-cause grob)
+ (let ((cause (ly:grob-property grob 'cause)))
+
+ (cond
+ ((ly:stream-event? cause) (non-event-cause cause))
+ ((ly:grob? cause) cause)
+ (else #f))))
+
(define-public (grob-interpret-markup grob text)
(let* ((layout (ly:grob-layout grob))
(defs (ly:output-def-lookup layout 'text-font-defaults))
(ly:side-position-interface::calc-cross-staff g)))
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; side-position stuff
+
+(define-public (only-if-beamed g)
+ (reduce lily-or
+ #f
+ (map (lambda (x)
+ (ly:grob? (ly:grob-object x 'beam)))
+ (ly:grob-array->list (ly:grob-object g
+ 'side-support-elements)))))
+
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; note heads
(define-public ((grob::calc-property-by-copy prop) grob)
(ly:event-property (event-cause grob) prop))
+(define-public ((grob::calc-property-by-non-event-cause prop) grob)
+ (ly:grob-property (non-event-cause grob) prop))
+
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; fret boards