/*
This file is part of LilyPond, the GNU music typesetter.
- Copyright (C) 2011 Mike Solomon <mike@apollinemike.com>
+ Copyright (C) 2011--2015 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
#include "item.hh"
#include "note-head.hh"
#include "pointer-group-interface.hh"
+#include "stem.hh"
class Beam_collision_engraver : public Engraver
{
protected:
- vector<Grob *> active_beams_;
- vector<Grob *> signaled_beams_;
- vector<Grob *> end_beams_;
- vector<Grob *> covered_grobs_;
- vector<Grob *> covered_interior_grobs_;
-
- DECLARE_ACKNOWLEDGER (note_head);
- DECLARE_ACKNOWLEDGER (accidental);
- DECLARE_ACKNOWLEDGER (clef);
- DECLARE_ACKNOWLEDGER (key_signature);
- DECLARE_ACKNOWLEDGER (time_signature);
- DECLARE_ACKNOWLEDGER (beam);
- DECLARE_END_ACKNOWLEDGER (beam);
- void stop_translation_timestep ();
+ vector<Grob_info> beams_;
+ vector<Grob_info> covered_grobs_;
+
+ void acknowledge_note_head (Grob_info);
+ void acknowledge_stem (Grob_info);
+ void acknowledge_accidental (Grob_info);
+ void acknowledge_clef (Grob_info);
+ void acknowledge_clef_modifier (Grob_info);
+ void acknowledge_key_signature (Grob_info);
+ void acknowledge_time_signature (Grob_info);
+ void acknowledge_beam (Grob_info);
+ void acknowledge_flag (Grob_info);
+
+ virtual void finalize ();
+
+private:
+ bool covered_grob_has_interface (Grob *covered_grob, Grob *beam);
+
public:
TRANSLATOR_DECLARATIONS (Beam_collision_engraver);
};
+Beam_collision_engraver::Beam_collision_engraver (Context *c)
+ : Engraver (c)
+{}
+
+bool
+Beam_collision_engraver::covered_grob_has_interface (Grob *covered_grob, Grob *beam)
+{
+ SCM interfaces = beam->get_property ("collision-interfaces");
+
+ for (SCM l = interfaces; scm_is_pair (l); l = scm_cdr (l))
+ {
+ if (covered_grob->internal_has_interface (scm_car (l)))
+ return true;
+ }
+
+ return false;
+}
+
void
-Beam_collision_engraver::stop_translation_timestep ()
+Beam_collision_engraver::finalize ()
{
- /*
- First, for all grobs that fall to the left of a beam during
- a timestep (i.e. clefs, time signatures), add these to
- the beams that are currently active.
- */
- for (vsize i = 0; i < covered_interior_grobs_.size (); i++)
- for (vsize j = 0; j < active_beams_.size (); j++)
- Pointer_group_interface::add_grob (active_beams_[j], ly_symbol2scm ("covered-grobs"), covered_interior_grobs_[i]);
-
- covered_interior_grobs_.clear ();
-
- /*
- If a signaled beam is already in active_beams_, we erase it so as
- not to have a beam represented in active_beams_ more than once.
- */
-
- for (vsize i = 0; i < active_beams_.size (); i++)
- for (vsize j = 0; j < signaled_beams_.size (); j++)
- if (active_beams_[i] == signaled_beams_[j])
- {
- signaled_beams_.erase (signaled_beams_.begin () + j);
- break;
- }
+ if (!covered_grobs_.size ())
+ return;
- /*
- In auto beaming, beams both begin and end during the same timestep.
- This means that if there is a beam that is both in signaled_beams_ and
- end_beams_, it must either be an auto beam (likely) or a beam that
- has no notes under it (highly unlikely). In either case, we cannot account
- for the grobs under this beam, and we erase it from signaled beams.
- */
- for (vsize i = 0; i < end_beams_.size (); i++)
- for (vsize j = 0; j < signaled_beams_.size (); j++)
- if (end_beams_[i] == signaled_beams_[j])
+ vector_sort (covered_grobs_, Grob_info::less);
+ vector_sort (beams_, Grob_info::less);
+ vsize start = 0;
+
+ for (vsize i = 0; i < beams_.size (); i++)
+ {
+ Grob *beam_grob = beams_[i].grob ();
+
+ extract_grob_set (beam_grob, "normal-stems", stems);
+ Interval_t<int> vertical_span;
+ for (vsize j = 0; j < stems.size (); j++)
{
- signaled_beams_.erase (signaled_beams_.begin () + j);
- break;
+ int vag = Grob::get_vertical_axis_group_index (stems[j]);
+ if (vag >= 0)
+ vertical_span.add_point (vag);
}
+ Context *beam_context = beams_[i].context ();
- /*
- We want to know how big active beams was originally so that we do not
- get any cyclical dependencies (see below).
- */
- vsize orig_size = active_beams_.size ();
-
- /*
- All signaled beams that are left now become active beams that are fair
- game to collect covered grobs.
- */
- for (vsize i=0; i < signaled_beams_.size (); i++)
- active_beams_.push_back (signaled_beams_[i]);
-
- /*
- Add all covered grobs that fall to the right of a beam (like noteheads)
- as to covered-grobs of the beam. Note that noteheads that part of a beam
- are not added to that list, as note heads should not never collide with
- their own beams due to minimum stem length penalties in beam-quanting.cc.
- */
- for (vsize i = 0; i < covered_grobs_.size (); i++)
- for (vsize j = 0; j < active_beams_.size (); j++)
- {
- bool my_beam = false;
- if (Grob *stem = unsmob_grob (covered_grobs_[i]->get_object ("stem")))
- if (Grob *beam = unsmob_grob (stem->get_object ("beam")))
- if (beam == active_beams_.at (j))
- my_beam = true;
- if (!my_beam)
- Pointer_group_interface::add_grob (active_beams_.at (j), ly_symbol2scm ("covered-grobs"), covered_grobs_[i]);
- }
-
- covered_grobs_.clear ();
-
- /*
- This is where cyclical dependencies are avoided. In beam collision avoidance,
- beams often need to avoid other beams. To do this, they need to know the beam's
- position. But, if that second beam needs to know the first beam's position, we
- have a cyclical dependency. So, we only ever add signaled beams to active_beams_
- that existed BEFORE this time step. This is controlled by the orig_size variable.
- The for loop stops before it gets to the signaled beams added above so that beams
- added during this timestep are never dependent on each other for positioning.
- */
- for (vsize i = 0; i < signaled_beams_.size (); i++)
- for (vsize j = 0; j < orig_size; j++)
- Pointer_group_interface::add_grob (active_beams_[j], ly_symbol2scm ("covered-grobs"), signaled_beams_[i]);
-
- signaled_beams_.clear ();
-
- /*
- If the end of a beam has been announced, it is no longer active. So, remove this beam
- from active_beams_.
- */
- for (vsize i = 0; i < end_beams_.size (); i++)
- for (vsize j = 0; j < active_beams_.size (); j++)
- if (end_beams_[i] == active_beams_[j])
+ Interval_t<int> beam_spanned_rank_ = beam_grob->spanned_rank_interval ();
+ // Start considering grobs at the first grob whose end falls at or after the beam's beginning.
+ while (covered_grobs_[start].grob ()->spanned_rank_interval ()[RIGHT] < beam_spanned_rank_[LEFT])
+ start++;
+
+ // Stop when the grob's beginning comes after the beam's end.
+ for (vsize j = start; j < covered_grobs_.size (); j++)
{
- active_beams_.erase (active_beams_.begin () + j);
- break;
+ Grob *covered_grob = covered_grobs_[j].grob ();
+ int vag = Grob::get_vertical_axis_group_index (covered_grob);
+ if (!vertical_span.contains (vag))
+ continue;
+ Context *covered_grob_context = covered_grobs_[j].context ();
+
+ Interval_t<int> covered_grob_spanned_rank = covered_grob->spanned_rank_interval ();
+ if (covered_grob_spanned_rank[LEFT] > beam_spanned_rank_[RIGHT])
+ break;
+ /*
+ Only consider grobs whose end falls at or after the beam's beginning.
+ If the grob is a beam, it cannot start before beams_[i].
+ Also, if the user wants to check for collisions only in the beam's voice,
+ then make sure the beam and the covered_grob are in the same voice.
+ */
+ if ((covered_grob_spanned_rank[RIGHT] >= beam_spanned_rank_[LEFT])
+ && !(to_boolean (beam_grob->get_property ("collision-voice-only"))
+ && (covered_grob_context != beam_context))
+ && !(has_interface<Beam> (covered_grob)
+ && (covered_grob_spanned_rank[LEFT] <= beam_spanned_rank_[LEFT]))
+ && covered_grob_has_interface (covered_grob, beam_grob))
+ {
+ // Do not consider note heads attached to the beam.
+ if (has_interface<Stem> (covered_grob))
+ if (unsmob<Grob> (covered_grob->get_object ("beam")))
+ continue;
+
+ if (Grob *stem = unsmob<Grob> (covered_grob->get_object ("stem")))
+ if (Grob *beam = unsmob<Grob> (stem->get_object ("beam")))
+ if (beam == beam_grob)
+ continue;
+
+ Pointer_group_interface::add_grob (beam_grob, ly_symbol2scm ("covered-grobs"), covered_grob);
+ }
}
-
- end_beams_.clear ();
+ }
}
-Beam_collision_engraver::Beam_collision_engraver () {}
-
void
Beam_collision_engraver::acknowledge_note_head (Grob_info i)
{
- covered_grobs_.push_back (i.grob ());
+ covered_grobs_.push_back (i);
+}
+
+void
+Beam_collision_engraver::acknowledge_stem (Grob_info i)
+{
+ covered_grobs_.push_back (i);
}
void
Beam_collision_engraver::acknowledge_accidental (Grob_info i)
{
- covered_grobs_.push_back (i.grob ());
+ if (i.grob ()->internal_has_interface (ly_symbol2scm ("inline-accidental-interface")))
+ covered_grobs_.push_back (i);
}
void
Beam_collision_engraver::acknowledge_clef (Grob_info i)
{
- covered_interior_grobs_.push_back (i.grob ());
+ covered_grobs_.push_back (i);
}
void
Beam_collision_engraver::acknowledge_key_signature (Grob_info i)
{
- covered_interior_grobs_.push_back (i.grob ());
+ covered_grobs_.push_back (i);
+}
+
+void
+Beam_collision_engraver::acknowledge_clef_modifier (Grob_info i)
+{
+ covered_grobs_.push_back (i);
}
void
Beam_collision_engraver::acknowledge_time_signature (Grob_info i)
{
- covered_interior_grobs_.push_back (i.grob ());
+ covered_grobs_.push_back (i);
}
void
-Beam_collision_engraver::acknowledge_beam (Grob_info i)
+Beam_collision_engraver::acknowledge_flag (Grob_info i)
{
- signaled_beams_.push_back (i.grob ());
+ covered_grobs_.push_back (i);
}
void
-Beam_collision_engraver::acknowledge_end_beam (Grob_info i)
+Beam_collision_engraver::acknowledge_beam (Grob_info i)
{
- end_beams_.push_back (i.grob ());
+ beams_.push_back (i);
+ covered_grobs_.push_back (i);
}
#include "translator.icc"
-ADD_ACKNOWLEDGER (Beam_collision_engraver, note_head);
-ADD_ACKNOWLEDGER (Beam_collision_engraver, accidental);
-ADD_ACKNOWLEDGER (Beam_collision_engraver, clef);
-ADD_ACKNOWLEDGER (Beam_collision_engraver, key_signature);
-ADD_ACKNOWLEDGER (Beam_collision_engraver, time_signature);
-ADD_ACKNOWLEDGER (Beam_collision_engraver, beam);
-ADD_END_ACKNOWLEDGER (Beam_collision_engraver, beam);
+
+void
+Beam_collision_engraver::boot ()
+{
+ ADD_ACKNOWLEDGER (Beam_collision_engraver, note_head);
+ ADD_ACKNOWLEDGER (Beam_collision_engraver, stem);
+ ADD_ACKNOWLEDGER (Beam_collision_engraver, accidental);
+ ADD_ACKNOWLEDGER (Beam_collision_engraver, clef);
+ ADD_ACKNOWLEDGER (Beam_collision_engraver, key_signature);
+ ADD_ACKNOWLEDGER (Beam_collision_engraver, time_signature);
+ ADD_ACKNOWLEDGER (Beam_collision_engraver, clef_modifier);
+ ADD_ACKNOWLEDGER (Beam_collision_engraver, flag);
+ ADD_ACKNOWLEDGER (Beam_collision_engraver, beam);
+}
ADD_TRANSLATOR (Beam_collision_engraver,
- /* doc */
- "Help beams avoid colliding with notes and clefs in other voices.",
+ /* doc */
+ "Help beams avoid colliding with notes and clefs in other voices.",
- /* create */
- "",
+ /* create */
+ "",
- /* read */
- "",
+ /* read */
+ "",
- /* write */
- ""
- );
+ /* write */
+ ""
+ );