]> git.donarmstrong.com Git - lilypond.git/commitdiff
Merge branch 'release/unstable' into HEAD
authorGraham Percival <graham@percival-music.ca>
Thu, 15 Dec 2011 17:03:32 +0000 (09:03 -0800)
committerGraham Percival <graham@percival-music.ca>
Thu, 15 Dec 2011 17:03:32 +0000 (09:03 -0800)
input/regression/dynamics-avoid-cross-staff-stem.ly [new file with mode: 0644]
lily/beam-quanting.cc
lily/include/interval-minefield.hh [new file with mode: 0644]
lily/include/self-alignment-interface.hh
lily/interval-minefield.cc [new file with mode: 0644]
lily/new-dynamic-engraver.cc
lily/self-alignment-interface.cc
scm/define-grob-properties.scm
scm/define-grobs.scm
scm/output-lib.scm

diff --git a/input/regression/dynamics-avoid-cross-staff-stem.ly b/input/regression/dynamics-avoid-cross-staff-stem.ly
new file mode 100644 (file)
index 0000000..fdf1a77
--- /dev/null
@@ -0,0 +1,16 @@
+\version "2.15.22"
+
+\header {
+  texidoc = "LilyPond automatically shifts dynamics that collide with
+cross-staff stems when manual beams are used."
+}
+
+\new GrandStaff <<
+  \new Staff = "PnRH" {
+    \relative g {
+      \stemDown gis8 \p [ \change Staff = "PnLH" \stemUp a, \fff ]
+      \change Staff = "PnRH" r4
+    }
+  }
+  \new Staff = "PnLH" { \clef "F" { s4 r4 } }
+>>
index 57cf25853dbbc49fd6ce3073b0f0f18172e76604..e971d6ee9d14f8a734105f80c896430363c436da 100644 (file)
@@ -33,6 +33,7 @@ using namespace std;
 #include "grob-array.hh"
 #include "item.hh"
 #include "international.hh"
+#include "interval-minefield.hh"
 #include "least-squares.hh"
 #include "libc-extension.hh"
 #include "main.hh"
@@ -811,42 +812,14 @@ Beam_scoring_problem::shift_region_to_valid ()
     }
 
   vector_sort (forbidden_intervals, Interval::left_less);
-  Real epsilon = 1.0e-10;
   Real beam_left_y = unquanted_y_[LEFT];
   Interval feasible_beam_placements (beam_left_y, beam_left_y);
 
-  /*
-    forbidden_intervals contains a vector of intervals in which
-    the beam cannot start.  it iterates through these intervals,
-    pushing feasible_beam_placements epsilon over or epsilon under a
-    collision.  when this type of change happens, the loop is marked
-    as "dirty" and re-iterated.
-
-    TODO: figure out a faster ways that this loop can happen via
-    a better search algorithm and/or OOP.
-  */
-
-  bool dirty = false;
-  do
-    {
-      dirty = false;
-      for (vsize i = 0; i < forbidden_intervals.size (); i++)
-        {
-          Direction d = DOWN;
-          do
-            {
-              if (forbidden_intervals[i][d] == d * infinity_f)
-                feasible_beam_placements[d] = d * infinity_f;
-              else if (forbidden_intervals[i].contains (feasible_beam_placements[d]))
-                {
-                  feasible_beam_placements[d] = d * epsilon + forbidden_intervals[i][d];
-                  dirty = true;
-                }
-            }
-          while (flip (&d) != DOWN);
-        }
-    }
-  while (dirty);
+  Interval_minefield minefield (feasible_beam_placements, 0.0);
+  for (vsize i = 0; i < forbidden_intervals.size (); i++)
+    minefield.add_forbidden_interval (forbidden_intervals[i]);
+  minefield.solve ();
+  feasible_beam_placements = minefield.feasible_placements ();
 
   // if the beam placement falls out of the feasible region, we push it
   // to infinity so that it can never be a feasible candidate below
diff --git a/lily/include/interval-minefield.hh b/lily/include/interval-minefield.hh
new file mode 100644 (file)
index 0000000..62136b0
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+  This file is part of LilyPond, the GNU music typesetter.
+
+  Copyright (C) 2011 Mike Solomon <mike@apollinemike.com>
+  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/>.
+*/
+
+#ifndef INTERVAL_MINEFIELD_HH
+#define INTERVAL_MINEFIELD_HH
+
+#include "lily-proto.hh"
+#include "std-vector.hh"
+#include "interval.hh"
+
+class Interval_minefield
+{
+public :
+  Interval_minefield (Interval, Real);
+  void add_forbidden_interval (Interval forbidden);
+  Interval feasible_placements ();
+  void solve ();
+
+private :
+  vector<Interval> forbidden_intervals_;
+  Interval feasible_placements_;
+  Real bulk_;
+};
+
+#endif // INTERVAL_MINEFIELD_HH
\ No newline at end of file
index 8ecde08a3e2f03b581e9611b44d8ae776935d570..80a7c0d37ac9d8511b6557fe89f8cf889caf8881 100644 (file)
@@ -30,8 +30,10 @@ struct Self_alignment_interface
   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));
@@ -41,7 +43,8 @@ struct Self_alignment_interface
   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));
 };
diff --git a/lily/interval-minefield.cc b/lily/interval-minefield.cc
new file mode 100644 (file)
index 0000000..5fe3414
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+  This file is part of LilyPond, the GNU music typesetter.
+
+  Copyright (C) 2011 Mike Solomon <mike@apollinemike.com>
+  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 "interval-minefield.hh"
+#include "grob.hh"
+Interval_minefield::Interval_minefield (Interval feasible_placements, Real bulk)
+{
+  feasible_placements_ = feasible_placements;
+  bulk_ = bulk;
+}
+
+void
+Interval_minefield::add_forbidden_interval (Interval forbidden)
+{
+  forbidden_intervals_.push_back (forbidden);
+}
+
+Interval
+Interval_minefield::feasible_placements ()
+{
+  return feasible_placements_;
+}
+
+  /*
+    forbidden_intervals_ contains a vector of intervals in which
+    the beam cannot start.  it iterates through these intervals,
+    pushing feasible_placements_ epsilon over or epsilon under a
+    collision.  when this type of change happens, the loop is marked
+    as "dirty" and re-iterated.
+
+    TODO: figure out a faster ways that this loop can happen via
+    a better search algorithm.
+  */
+void
+Interval_minefield::solve()
+{
+  Real epsilon = 1.0e-10;
+  bool dirty = false;
+  do
+    {
+      dirty = false;
+      for (vsize i = 0; i < forbidden_intervals_.size (); i++)
+        {
+          Direction d = DOWN;
+          do
+            {
+              Interval feasible_widened = Interval (feasible_placements_[d], feasible_placements_[d]);
+              feasible_widened.widen (bulk_ / 2.);
+
+              if (forbidden_intervals_[i][d] == d * infinity_f)
+                feasible_placements_[d] = d * infinity_f;
+              else if (forbidden_intervals_[i].contains (feasible_widened[d])
+                       || forbidden_intervals_[i].contains (feasible_widened[-d])
+                       || feasible_widened.contains (forbidden_intervals_[i][d])
+                       || feasible_widened.contains (forbidden_intervals_[i][-d]))
+                {
+                  feasible_placements_[d] = forbidden_intervals_[i][d] + d * (epsilon +  (bulk_ / 2));
+                  dirty = true;
+                }
+            }
+          while (flip (&d) != DOWN);
+        }
+    }
+  while (dirty);
+}
\ No newline at end of file
index 3554b22e9ee00a290fabdf0c47eb8245e3e5f74a..1418e563f5aad1e10a4865770b879315f7a40e0b 100644 (file)
@@ -260,6 +260,7 @@ New_dynamic_engraver::acknowledge_note_column (Grob_info info)
   if (script_ && !script_->get_parent (X_AXIS))
     {
       extract_grob_set (info.grob (), "note-heads", heads);
+      Grob *stem = unsmob_grob (info.grob ()->get_object ("stem"));
       /*
         Spacing constraints may require dynamics to be aligned on rests,
         so check for a rest if this note column has no note heads.
@@ -271,7 +272,10 @@ New_dynamic_engraver::acknowledge_note_column (Grob_info info)
         {
           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);
     }
 
   if (current_spanner_ && !current_spanner_->get_bound (LEFT))
index 0c7215233338ebb32c277e2af1a2c88ec154c216..a6fde153e043e826b4865c467c3267d6bdeb1685 100644 (file)
 
 #include "self-alignment-interface.hh"
 
+#include "directional-element-interface.hh"
 #include "grob.hh"
+#include "grob-array.hh"
+#include "interval-minefield.hh"
 #include "paper-column.hh"
+#include "pointer-group-interface.hh"
 #include "warn.hh"
 
 MAKE_SCHEME_CALLBACK (Self_alignment_interface, y_aligned_on_self, 1);
@@ -134,6 +138,69 @@ Self_alignment_interface::aligned_on_parent (Grob *me, Axis a)
   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++)
+    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)
 {
@@ -142,6 +209,12 @@ 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)
 {
@@ -165,6 +238,11 @@ ADD_INTERFACE (Self_alignment_interface,
                "@end table\n",
 
                /* properties */
+               "collision-bias "
+               "collision-padding "
+               "potential-X-colliding-grobs "
                "self-alignment-X "
                "self-alignment-Y "
+               "X-colliding-grobs "
+               "Y-colliding-grobs "
               );
index c26bd3cae19a503c10ee9e3eb7c586e8498adc9a..f209fe649ac63f42306339cb40ea9658934d770d 100644 (file)
@@ -174,8 +174,13 @@ hairpins (al/@/del niente).")
 edges of beams?")
      (collapse-height ,ly:dimension? "Minimum height of system start
 delimiter.  If equal or smaller, the bracket/@/brace/@/line is removed.")
+     (collision-bias ,number? "Number determining how much to favor the
+left (negative) or right (positive).  Larger absolute values in either
+direction will push a collision in this direction.")
      (collision-interfaces ,list? "A list of interfaces for which
 automatic beam-collision resolution is run.")
+     (collision-padding ,number? "Amount of padding to apply after
+a collision is detected via the self-alignment-interface.")
      (collision-voice-only ,boolean? "Does automatic beam collsion apply
 only to the voice in which the beam was created?")
      (color ,color? "The color of this grob.")
@@ -1070,6 +1075,8 @@ pure-from-neighbor-interface to determine various grob heights.")
      (note-heads ,ly:grob-array? "An array of note head grobs.")
      (pedal-text ,ly:grob? "A pointer to the text of a mixed-style piano
 pedal.")
+     (potential-X-colliding-grobs ,ly:grob-array? "Grobs that can potentially
+collide with a self-aligned grob on the X-axis.")
      (pure-relevant-grobs ,ly:grob-array? "All the grobs (items and spanners)
 that are relevant for finding the @code{pure-Y-extent}")
      (pure-relevant-items ,ly:grob-array? "A subset of elements that are
@@ -1110,6 +1117,10 @@ results, use @code{LEFT} and @code{RIGHT}.")
      (tremolo-flag ,ly:grob? "The tremolo object on a stem.")
      (tuplet-number ,ly:grob? "The number for a bracket.")
      (tuplets ,ly:grob-array? "An array of smaller tuplet brackets.")
+     (X-colliding-grobs ,ly:grob-array? "Grobs that can collide
+with a self-aligned grob on the X-axis.")
+     (Y-colliding-grobs ,ly:grob-array? "Grobs that can collide
+with a self-aligned grob on the Y-axis.")
      (X-common ,ly:grob? "Common reference point for axis group.")
      (Y-common ,ly:grob? "See @code{X-common}.")
 
index 653bc5e227046d944a281a5d34558f68005e4007..806f720697814d4094e08348b7fc2d6fd9f02230 100644 (file)
 
        ;; todo.
 
+       (collision-bias . -2.0)
+       (collision-padding . 0.5)
        (direction . ,ly:script-interface::calc-direction)
        (extra-spacing-width . (+inf.0 . -inf.0))
        (font-encoding . fetaText)
        (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
index 3e2d5ff5201e9a103e49b08b8aa61983c91270fe..9137d5be170845fd9e0d9cd498dc425c01575982 100644 (file)
@@ -58,7 +58,6 @@
 
     (ly:text-interface::interpret-markup layout props text)))
 
-
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;; beam slope