From 44494930c763c572dbbe45111088c85381299505 Mon Sep 17 00:00:00 2001
From: Mike Solomon <mike@apollinemike.com>
Date: Sat, 8 Jan 2011 12:24:33 -0500
Subject: [PATCH 1/6] Fixing issue 37 with extra position callback

Adds beam collision engraver

Goodbye whitespace
---
 lily/beam-collision-engraver.cc |  114 +++++++++++++++++++++++++++++++++++++++
 lily/beam.cc                    |   80 +++++++++++++++++++++++++++
 lily/include/beam.hh            |    1 +
 ly/engraver-init.ly             |    1 +
 scm/define-grobs.scm            |    1 +
 5 files changed, 197 insertions(+), 0 deletions(-)
 create mode 100644 lily/beam-collision-engraver.cc

diff --git a/lily/beam-collision-engraver.cc b/lily/beam-collision-engraver.cc
new file mode 100644
index 0000000..7b8ad9c
--- /dev/null
+++ b/lily/beam-collision-engraver.cc
@@ -0,0 +1,114 @@
+/*
+  This file is part of LilyPond, the GNU music typesetter.
+
+  Copyright (C) 1997--2010 Han-Wen Nienhuys <hanwen@xs4all.nl>
+
+  LilyPond is free software: you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation, either version 3 of the License, or
+  (at your option) any later version.
+
+  LilyPond is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with LilyPond.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "beam.hh"
+#include "engraver.hh"
+#include "note-head.hh"
+#include "pointer-group-interface.hh"
+#include "item.hh"
+#include "international.hh"
+
+class Beam_collision_engraver : public Engraver
+{
+
+protected:
+
+  vector<Grob *> active_beams_;
+  vector<Grob *> end_beams_;
+  vector<Grob *> note_heads_;
+
+  DECLARE_ACKNOWLEDGER (note_head);
+  DECLARE_ACKNOWLEDGER (beam);
+  DECLARE_END_ACKNOWLEDGER (beam);
+  void process_acknowledged ();
+  void stop_translation_timestep ();
+public:
+  TRANSLATOR_DECLARATIONS (Beam_collision_engraver);
+};
+
+void
+Beam_collision_engraver::stop_translation_timestep() {
+  for (vsize i=0; i < note_heads_.size (); i++)
+  {
+    for (vsize j=0; j < active_beams_.size (); j++)
+      Pointer_group_interface::add_grob (active_beams_.at (j), ly_symbol2scm ("covering-note-heads"), note_heads_.at (i));
+  }
+  note_heads_.clear ();
+
+  for (vsize i=0; i < end_beams_.size (); i++)
+  {
+    for (vsize j=0; j < active_beams_.size (); j++)
+    {
+      if (end_beams_.at (i) == active_beams_.at (j))
+      {
+        active_beams_.erase (active_beams_.begin() + j);
+        break;
+      }
+    }
+  }
+  end_beams_.clear ();
+}
+
+Beam_collision_engraver::Beam_collision_engraver() {}
+
+void
+Beam_collision_engraver::process_acknowledged() {}
+
+void
+Beam_collision_engraver::acknowledge_note_head (Grob_info i)
+{
+  note_heads_.push_back (i.grob ());
+}
+
+void
+Beam_collision_engraver::acknowledge_beam (Grob_info i)
+{
+  for (vsize j=0; j < active_beams_.size (); j++)
+  {
+    if (i.grob () == active_beams_.at (j))
+      return;
+  }
+  active_beams_.push_back (i.grob ());
+}
+
+void
+Beam_collision_engraver::acknowledge_end_beam (Grob_info i)
+{
+  end_beams_.push_back (i.grob ());
+}
+
+#include "translator.icc"
+
+ADD_ACKNOWLEDGER (Beam_collision_engraver, note_head);
+ADD_ACKNOWLEDGER (Beam_collision_engraver, beam);
+ADD_END_ACKNOWLEDGER (Beam_collision_engraver, beam);
+
+ADD_TRANSLATOR (Beam_collision_engraver,
+		/* doc */
+		"Help beams avoid colliding with notes in other voices.",
+
+		/* create */
+		"",
+
+		/* read */
+		"",
+
+		/* write */
+		""
+		);
diff --git a/lily/beam.cc b/lily/beam.cc
index dfc33cb..506e0a0 100644
--- a/lily/beam.cc
+++ b/lily/beam.cc
@@ -927,7 +927,87 @@ Beam::no_visible_stem_positions (Grob *me, Interval default_value)
   return Interval (y,y);
 }
 
+/*
+  Raise the quanted beam for collisions.
+*/
+MAKE_SCHEME_CALLBACK (Beam, move_to_avoid_collisions, 2);
+SCM
+Beam::move_to_avoid_collisions (SCM smob, SCM posns)
+{
+
+  Grob *me = unsmob_grob (smob);
+  Direction fd = to_dir (first_normal_stem (me)->get_property ("direction"));
+  Direction ld = to_dir (last_normal_stem (me)->get_property ("direction"));
 
+
+  if (fd == ld & !is_cross_staff (me))
+  {
+    Interval has_acc(0.5,-0.5);
+    Interval lacks_acc(0.0,0.0);
+    Direction dummy = LEFT;
+    Real offset = 0.0;
+    Drul_array<Real> pos = ly_scm2interval (posns);
+    extract_grob_set (me, "covering-note-heads", covering_note_heads);
+    Real magic = 0.0;
+
+    extract_grob_set (me, "normal-stems", stems);
+    Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
+    Grob *commony = common_refpoint_of_array (stems, me, Y_AXIS);
+    Real x0 = first_normal_stem (me)->relative_coordinate (commonx, X_AXIS);
+    Real xlast = last_normal_stem (me)->relative_coordinate (commonx, X_AXIS);
+
+    Real slope = (pos[LEFT] - pos[RIGHT]) / (x0 - xlast);
+
+    if (fd == UP)
+    {
+      magic = 2.0;
+      dummy = LEFT;
+    }
+    else {
+      magic = -2.0;
+      dummy = RIGHT;
+    }
+
+    for (vsize i = 0; i < covering_note_heads.size (); i++)
+    {
+      Grob *s = covering_note_heads.at (i);
+      Grob *acc = unsmob_grob (s->get_object ("accidental-grob"));
+      Real acc_offset = acc ? has_acc[dummy] : lacks_acc[dummy];
+
+      Real y_val = s->relative_coordinate (commony, Y_AXIS) + magic + acc_offset - pos[LEFT];
+
+      Real x_val1 = s->relative_coordinate (commonx, X_AXIS) - x0;
+
+      Real x_val2 = 0.0;
+      Stencil *nh_stencil = unsmob_stencil (s->get_property ("stencil"));
+      if (nh_stencil)
+        x_val2 = x_val1 + nh_stencil->extent(X_AXIS)[RIGHT];
+      else
+        x_val2 = x_val1;
+
+      Real beam_y1 = slope * x_val1;
+      Real beam_y2 = slope * x_val2;
+
+      if (fd == UP)
+      {
+        if (y_val > beam_y1)
+          offset = max (y_val - beam_y1, offset);
+        if (y_val > beam_y2)
+          offset = max (y_val - beam_y2, offset);
+      } else {
+        if (y_val < beam_y1)
+          offset = min(y_val - beam_y1, offset);
+        if (y_val < beam_y2)
+          offset = min(y_val - beam_y2, offset);
+      }
+    }
+
+    pos[LEFT] += offset;
+    pos[RIGHT] += offset;
+    return ly_interval2scm (pos);
+  } else
+    return posns;
+}
 /*
   Compute a first approximation to the beam slope.
 */
diff --git a/lily/include/beam.hh b/lily/include/beam.hh
index c40bcc9..7723b65 100644
--- a/lily/include/beam.hh
+++ b/lily/include/beam.hh
@@ -102,6 +102,7 @@ public:
   DECLARE_SCHEME_CALLBACK (calc_direction, (SCM));
   DECLARE_SCHEME_CALLBACK (calc_positions, (SCM));
   DECLARE_SCHEME_CALLBACK (calc_least_squares_positions, (SCM, SCM));
+  DECLARE_SCHEME_CALLBACK (move_to_avoid_collisions, (SCM, SCM));
   DECLARE_SCHEME_CALLBACK (calc_normal_stems, (SCM));  
   DECLARE_SCHEME_CALLBACK (calc_concaveness, (SCM));
   DECLARE_SCHEME_CALLBACK (set_stem_lengths, (SCM));
diff --git a/ly/engraver-init.ly b/ly/engraver-init.ly
index 83d313b..d453b8f 100644
--- a/ly/engraver-init.ly
+++ b/ly/engraver-init.ly
@@ -65,6 +65,7 @@
   \consists "Ledger_line_engraver"
   \consists "Staff_symbol_engraver"
   \consists "Collision_engraver"
+  \consists "Beam_collision_engraver"
   \consists "Grob_pq_engraver"
   \consists "Rest_collision_engraver"
   \consists "Accidental_engraver"
diff --git a/scm/define-grobs.scm b/scm/define-grobs.scm
index ff076f8..52b9791 100644
--- a/scm/define-grobs.scm
+++ b/scm/define-grobs.scm
@@ -377,6 +377,7 @@
 			   ly:beam::slope-damping
 			   ly:beam::shift-region-to-valid
 			   ly:beam::quanting
+                           ly:beam::move-to-avoid-collisions
 			   ))))
 
 	;; this is a hack to set stem lengths, if positions is set.
-- 
1.7.3.2

