]> git.donarmstrong.com Git - lilypond.git/blobdiff - lily/accidental-placement.cc
add vcs lines to debian/control
[lilypond.git] / lily / accidental-placement.cc
index 0f14cc9a83e94e021e173075c6b1dfa4ddc9b7ff..4a535c785783d937e3112d8221fef5dbbe2381a2 100644 (file)
@@ -1,42 +1,59 @@
 /*
-  accidental-placement.cc -- implement Accidental_placement
+  This file is part of LilyPond, the GNU music typesetter.
 
-  source file of the GNU LilyPond music typesetter
+  Copyright (C) 2002--2011 Han-Wen Nienhuys <hanwen@xs4all.nl>
 
-  (c) 2002--2009 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 "accidental-placement.hh"
 
-#include "item.hh"
-#include "rhythmic-head.hh"
 #include "accidental-interface.hh"
+#include "item.hh"
 #include "music.hh"
 #include "note-collision.hh"
 #include "note-column.hh"
 #include "pointer-group-interface.hh"
+#include "rhythmic-head.hh"
 #include "skyline.hh"
 #include "stream-event.hh"
 #include "warn.hh"
 
-
-void
-Accidental_placement::add_accidental (Grob *me, Grob *a)
+static Pitch *
+accidental_pitch (Grob *acc)
 {
-  a->set_parent (me, X_AXIS);
-  a->set_property ("X-offset", Grob::x_parent_positioning_proc);
-  SCM cause = a->get_parent (Y_AXIS)->get_property ("cause");
+  SCM cause = acc->get_parent (Y_AXIS)->get_property ("cause");
 
   Stream_event *mcause = unsmob_stream_event (cause);
   if (!mcause)
     {
       programming_error ("note head has no event cause");
-      return;
+      return 0;
     }
 
-  Pitch *p = unsmob_pitch (mcause->get_property ("pitch"));
+  return unsmob_pitch (mcause->get_property ("pitch"));
+}
+
+void
+Accidental_placement::add_accidental (Grob *me, Grob *a)
+{
+  Pitch *p = accidental_pitch (a);
+  if (!p)
+    return;
 
+  a->set_parent (me, X_AXIS);
+  a->set_property ("X-offset", Grob::x_parent_positioning_proc);
   int n = p->get_notename ();
 
   SCM accs = me->get_object ("accidental-grobs");
@@ -59,8 +76,8 @@ Accidental_placement::add_accidental (Grob *me, Grob *a)
 */
 void
 Accidental_placement::split_accidentals (Grob *accs,
-                                        vector<Grob*> *break_reminder,
-                                        vector<Grob*> *real_acc)
+                                        vector<Grob *> *break_reminder,
+                                        vector<Grob *> *real_acc)
 {
   for (SCM acs = accs->get_object ("accidental-grobs"); scm_is_pair (acs);
        acs = scm_cdr (acs))
@@ -76,18 +93,18 @@ Accidental_placement::split_accidentals (Grob *accs,
       }
 }
 
-vector<Grob*>
-Accidental_placement::get_relevant_accidentals (vector<Grob*> const &elts, Grob *left)
+vector<Grob *>
+Accidental_placement::get_relevant_accidentals (vector<Grob *> const &elts, Grob *left)
 {
-  vector<Grob*> br;
-  vector<Grob*> ra;
-  vector<Grob*> ret;
+  vector<Grob *> br;
+  vector<Grob *> ra;
+  vector<Grob *> ret;
   bool right = dynamic_cast<Item *> (left)->break_status_dir () == RIGHT;
 
   for (vsize i = 0; i < elts.size (); i++)
     {
       split_accidentals (elts[i], &br, &ra);
-      
+
       ret.insert (ret.end (), ra.begin (), ra.end ());
 
       if (right)
@@ -102,7 +119,7 @@ struct Accidental_placement_entry
   Skyline right_skyline_;
   Interval vertical_extent_;
   vector<Box> extents_;
-  vector<Grob*> grobs_;
+  vector<Grob *> grobs_;
 };
 
 Real ape_priority (Accidental_placement_entry const *a)
@@ -110,36 +127,59 @@ Real ape_priority (Accidental_placement_entry const *a)
   return a->vertical_extent_[UP];
 }
 
-int ape_compare (Accidental_placement_entry *const &a,
-                Accidental_placement_entry *const &b)
-{
-  return sign (ape_priority (a) - ape_priority (b));
-}
-
 bool ape_less (Accidental_placement_entry *const &a,
               Accidental_placement_entry *const &b)
 {
   return ape_priority (a) < ape_priority (b);
 }
 
-int ape_rcompare (Accidental_placement_entry *const &a,
-                 Accidental_placement_entry *const &b)
+/*
+  This function provides a method for sorting accidentals that belong to the
+  same note. The accidentals that this function considers to be "smallest"
+  will be placed to the left of the "larger" accidentals.
+
+  Naturals are the largest (so that they don't get confused with cancellation
+  naturals); apart from that, we order according to the alteration (so
+  double-flats are the smallest).
+
+  Precondition: the accidentals are attached to NoteHeads of the same note
+  name -- the octaves, however, may be different.
+*/
+static bool
+acc_less (Grob *const &a, Grob *const &b)
 {
-  return -sign (ape_priority (a) - ape_priority (b));
+  Pitch *p = accidental_pitch (a);
+  Pitch *q = accidental_pitch (b);
+
+  if (!p || !q)
+    {
+      programming_error ("these accidentals do not have a pitch");
+      return false;
+    }
+
+  if (p->get_octave () != q->get_octave ())
+    return p->get_octave () < q->get_octave ();
+
+  if (p->get_alteration () == Rational (0))
+    return false;
+  if (q->get_alteration () == Rational (0))
+    return true;
+
+  return p->get_alteration () < q->get_alteration ();
 }
 
 /*
   TODO: should favor
 
-  b
-  b
+  *  b
+  b
 
   placement
 */
 void
-stagger_apes (vector<Accidental_placement_entry*> *apes)
+stagger_apes (vector<Accidental_placement_entry *> *apes)
 {
-  vector<Accidental_placement_entry*> asc = *apes;
+  vector<Accidental_placement_entry *> asc = *apes;
 
   vector_sort (asc, &ape_less);
 
@@ -164,10 +204,10 @@ stagger_apes (vector<Accidental_placement_entry*> *apes)
   reverse (*apes);
 }
 
-static vector<Accidental_placement_entry*>
+static vector<Accidental_placement_entry *>
 build_apes (SCM accs)
 {
-  vector<Accidental_placement_entry*> apes;
+  vector<Accidental_placement_entry *> apes;
   for (SCM s = accs; scm_is_pair (s); s = scm_cdr (s))
     {
       Accidental_placement_entry *ape = new Accidental_placement_entry;
@@ -183,26 +223,62 @@ build_apes (SCM accs)
 
 static void
 set_ape_skylines (Accidental_placement_entry *ape,
-                 Grob **common)
+                 Grob **common, Real padding)
 {
-  for (vsize i = ape->grobs_.size (); i--;)
+  vector<Grob *> accs (ape->grobs_);
+  vector_sort (accs, &acc_less);
+
+  /* We know that each accidental has the same note name and we assume that
+     accidentals in different octaves won't collide. If two or more
+     accidentals are in the same octave:
+     1) if they are the same accidental, print them in overstrike
+     2) otherwise, shift one to the left so they don't overlap. */
+  int last_octave = 0;
+  Real offset = 0;
+  Real last_offset = 0;
+  Rational last_alteration (0);
+  for (vsize i = accs.size (); i--;)
     {
-      Grob *a = ape->grobs_[i];
+      Grob *a = accs[i];
+      Pitch *p = accidental_pitch (a);
+
+      if (!p)
+       continue;
+
+      if (i == accs.size () - 1 || p->get_octave () != last_octave)
+       {
+         last_offset = 0;
+         offset = a->extent (a, X_AXIS)[LEFT] - padding;
+       }
+      else if (p->get_alteration () == last_alteration)
+       a->translate_axis (last_offset, X_AXIS);
+      else /* Our alteration is different from the last one */
+       {
+         Real this_offset = offset - a->extent (a, X_AXIS)[RIGHT];
+         a->translate_axis (this_offset, X_AXIS);
+
+         last_offset = this_offset;
+         offset -= a->extent (a, X_AXIS).length () + padding;
+       }
+
       vector<Box> boxes = Accidental_interface::accurate_boxes (a, common);
       ape->extents_.insert (ape->extents_.end (), boxes.begin (), boxes.end ());
 
       for (vsize j = boxes.size (); j--;)
        ape->vertical_extent_.unite (boxes[j][Y_AXIS]);
+
+      last_octave = p->get_octave ();
+      last_alteration = p->get_alteration ();
     }
   ape->left_skyline_ = Skyline (ape->extents_, 0, Y_AXIS, LEFT);
   ape->right_skyline_ = Skyline (ape->extents_, 0, Y_AXIS, RIGHT);
 }
 
-static vector<Grob*>
-extract_heads_and_stems (vector<Accidental_placement_entry*> const &apes)
+static vector<Grob *>
+extract_heads_and_stems (vector<Accidental_placement_entry *> const &apes)
 {
-  vector<Grob*> note_cols;
-  vector<Grob*> ret;
+  vector<Grob *> note_cols;
+  vector<Grob *> ret;
 
   for (vsize i = apes.size (); i--;)
     {
@@ -243,14 +319,13 @@ extract_heads_and_stems (vector<Accidental_placement_entry*> const &apes)
     if (Grob *s = Rhythmic_head::get_stem (ret[i]))
       ret.push_back (s);
 
-
-  vector_sort (ret, less<Grob*> ());
+  vector_sort (ret, less<Grob *> ());
   uniq (ret);
   return ret;
 }
 
-static Grob*
-common_refpoint_of_accidentals (vector<Accidental_placement_entry*> const &apes, Axis a)
+static Grob *
+common_refpoint_of_accidentals (vector<Accidental_placement_entry *> const &apes, Axis a)
 {
   Grob *ret = 0;
 
@@ -267,7 +342,7 @@ common_refpoint_of_accidentals (vector<Accidental_placement_entry*> const &apes,
 }
 
 static Skyline
-build_heads_skyline (vector<Grob*> const &heads_and_stems,
+build_heads_skyline (vector<Grob *> const &heads_and_stems,
                     Grob **common)
 {
   vector<Box> head_extents;
@@ -284,13 +359,13 @@ build_heads_skyline (vector<Grob*> const &heads_and_stems,
 */
 static Interval
 position_apes (Grob *me,
-              vector<Accidental_placement_entry*> const &apes,
+              vector<Accidental_placement_entry *> const &apes,
               Skyline const &heads_skyline)
 {
   Real padding = robust_scm2double (me->get_property ("padding"), 0.2);
   Skyline left_skyline = heads_skyline;
   left_skyline.raise (-robust_scm2double (me->get_property ("right-padding"), 0));
-  
+
   /*
     Add accs entries right-to-left.
   */
@@ -324,7 +399,6 @@ position_apes (Grob *me,
   return width;
 }
 
-
 /*
   This routine computes placements of accidentals. During
   add_accidental (), accidentals are already grouped by note, so that
@@ -337,12 +411,11 @@ position_apes (Grob *me,
   TODO: more advanced placement. Typically, the accs should be placed
   to form a C shape, like this
 
-
-  ##
-  b b
-  # #
-  b
-  b b
+  *     ##
+  *  b b
+  * # #
+  *  b
+  *    b b
 
   The naturals should be left of the C as well; they should
   be separate accs.
@@ -375,23 +448,24 @@ Accidental_placement::calc_positioning_done (SCM smob)
     return SCM_BOOL_T;
 
   me->set_property ("positioning-done", SCM_BOOL_T);
-  
+
   SCM accs = me->get_object ("accidental-grobs");
   if (!scm_is_pair (accs))
     return SCM_BOOL_T;
 
-  vector<Accidental_placement_entry*> apes = build_apes (accs);
+  vector<Accidental_placement_entry *> apes = build_apes (accs);
 
   Grob *common[] = {me, 0};
 
-  vector<Grob*> heads_and_stems = extract_heads_and_stems (apes);
+  vector<Grob *> heads_and_stems = extract_heads_and_stems (apes);
 
   common[Y_AXIS] = common_refpoint_of_accidentals (apes, Y_AXIS);
   common[Y_AXIS] = common_refpoint_of_array (heads_and_stems, common[Y_AXIS], Y_AXIS);
   common[X_AXIS] = common_refpoint_of_array (heads_and_stems, me, X_AXIS);
+  Real padding = robust_scm2double (me->get_property ("padding"), 0.2);
 
   for (vsize i = apes.size (); i--;)
-    set_ape_skylines (apes[i], common);
+    set_ape_skylines (apes[i], common, padding);
   Skyline heads_skyline = build_heads_skyline (heads_and_stems, common);
 
   stagger_apes (&apes);
@@ -411,9 +485,8 @@ ADD_INTERFACE (Accidental_placement,
               /* properties */
               "accidental-grobs "
               "direction "
-              "left-padding "
               "padding "
               "positioning-done "
               "right-padding "
               "script-priority "
-              )
+              );