]> git.donarmstrong.com Git - lilypond.git/blobdiff - lily/beam-collision-engraver.cc
Run grand-replace (issue 3765)
[lilypond.git] / lily / beam-collision-engraver.cc
index 99f9298209f6b9a57cff2d27abd5e1c8b1275917..35ef3e315184ea06aff9ee872a4a5811bfc73cac 100644 (file)
@@ -1,7 +1,7 @@
 /*
   This file is part of LilyPond, the GNU music typesetter.
 
-  Copyright (C) 2011 Mike Solomon <mike@apollinemike.com>
+  Copyright (C) 2011--2014 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_;
+  vector<Grob_info> beams_;
+  vector<Grob_info> covered_grobs_;
 
   DECLARE_ACKNOWLEDGER (note_head);
+  DECLARE_ACKNOWLEDGER (stem);
   DECLARE_ACKNOWLEDGER (accidental);
   DECLARE_ACKNOWLEDGER (clef);
+  DECLARE_ACKNOWLEDGER (clef_modifier);
   DECLARE_ACKNOWLEDGER (key_signature);
   DECLARE_ACKNOWLEDGER (time_signature);
-  DECLARE_ACKNOWLEDGER (bar_line);
   DECLARE_ACKNOWLEDGER (beam);
-  DECLARE_END_ACKNOWLEDGER (beam);
-  void stop_translation_timestep ();
+  DECLARE_ACKNOWLEDGER (flag);
+
+  virtual void finalize ();
+
+private:
+  bool covered_grob_has_interface (Grob *covered_grob, Grob *beam);
+
 public:
   TRANSLATOR_DECLARATIONS (Beam_collision_engraver);
 };
 
-void
-Beam_collision_engraver::stop_translation_timestep ()
-{
-  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]);
+Beam_collision_engraver::Beam_collision_engraver () {}
 
-  covered_interior_grobs_.clear ();
+bool
+Beam_collision_engraver::covered_grob_has_interface (Grob *covered_grob, Grob *beam)
+{
+  SCM interfaces = beam->get_property ("collision-interfaces");
 
-  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;
-        }
+  for (SCM l = interfaces; scm_is_pair (l); l = scm_cdr (l))
+    {
+      if (covered_grob->internal_has_interface (scm_car (l)))
+        return true;
+    }
 
-  /*
-    hack.
-    in auto beaming, end beams are signaled with their beams at a later timestep.
-    we need to scrub these.
-  */
-  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])
-        {
-          signaled_beams_.erase (signaled_beams_.begin () + j);
-          break;
-        }
+  return false;
+}
 
-  for (vsize i = 0; i < signaled_beams_.size (); i++)
-    active_beams_.push_back (signaled_beams_[i]);
+void
+Beam_collision_engraver::finalize ()
+{
+  if (!covered_grobs_.size ())
+    return;
 
-  signaled_beams_.clear ();
+  vector_sort (covered_grobs_, Grob_info::less);
+  vector_sort (beams_, Grob_info::less);
+  vsize start = 0;
 
-  for (vsize i = 0; i < covered_grobs_.size (); i++)
-    for (vsize j = 0; j < active_beams_.size (); j++)
-      {
-        Grob *g = covered_grobs_[i];
-        if (Grob *stem = unsmob_grob (g->get_object ("stem")))
-          if (Grob *beam = unsmob_grob (stem->get_object ("beam")))
-            if (beam == active_beams_[j])
-              continue;
+  for (vsize i = 0; i < beams_.size (); i++)
+    {
+      Grob *beam_grob = beams_[i].grob ();
 
-        Pointer_group_interface::add_grob (active_beams_[j], ly_symbol2scm ("covered-grobs"), g);
-      }
+      extract_grob_set (beam_grob, "normal-stems", stems);
+      Interval_t<int> vertical_span;
+      for (vsize j = 0; j < stems.size (); j++)
+        {
+          int vag = Grob::get_vertical_axis_group_index (stems[j]);
+          if (vag >= 0)
+            vertical_span.add_point (vag);
+        }
+      Context *beam_context = beams_[i].context ();
 
-  covered_grobs_.clear ();
+      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++;
 
-  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])
+      // 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))
+              && !(Beam::has_interface (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 (Stem::has_interface (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_accidental (Grob_info i)
+Beam_collision_engraver::acknowledge_stem (Grob_info i)
 {
-  covered_grobs_.push_back (i.grob ());
+  covered_grobs_.push_back (i);
 }
 
 void
-Beam_collision_engraver::acknowledge_bar_line (Grob_info i)
+Beam_collision_engraver::acknowledge_accidental (Grob_info i)
 {
-  covered_interior_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, 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_ACKNOWLEDGER (Beam_collision_engraver, bar_line);
-ADD_END_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 */
+                ""
+               );