]> git.donarmstrong.com Git - lilypond.git/blobdiff - lily/beam.cc
release: 1.0.1
[lilypond.git] / lily / beam.cc
index e792aeddbd34b65fbf471d4a775a1ae033b8255d..dea856499bc00ce6f59d42c0dd87c9d277487330 100644 (file)
@@ -3,8 +3,8 @@
 
   source file of the GNU LilyPond music typesetter
 
-  (c)  1997--1998, 1998 Han-Wen Nienhuys <hanwen@stack.nl>
-    Jan Nieuwenhuizen <jan@digicash.com>
+  (c)  1997--1998, 1998 Han-Wen Nienhuys <hanwen@cs.uu.nl>
+    Jan Nieuwenhuizen <janneke@gnu.org>
 
 */
 
     * centre beam symbol
     * less hairy code
     * redo grouping 
-    * (future) knee: ([\stem 1; c8 \stem -1; c8]
  */
 
 #include <math.h>
 
 #include "p-col.hh"
-#include "varray.hh"
+#include "array.hh"
 #include "proto.hh"
-#include "dimen.hh"
+#include "dimension.hh"
 #include "beam.hh"
 #include "abbreviation-beam.hh"
 #include "misc.hh"
@@ -35,7 +34,6 @@
 #include "lookup.hh"
 #include "grouping.hh"
 #include "stem-info.hh"
-#include "main.hh"  // experimental features
 
 
 IMPLEMENT_IS_TYPE_B1 (Beam, Spanner);
@@ -43,6 +41,7 @@ IMPLEMENT_IS_TYPE_B1 (Beam, Spanner);
 Beam::Beam ()
 {
   slope_f_ = 0;
+  solved_slope_f_ = 0;
   left_y_ = 0;
   damping_i_ = 1;
   quantisation_ = NORMAL;
@@ -50,7 +49,7 @@ Beam::Beam ()
 }
 
 void
-Beam::add (Stem*s)
+Beam::add_stem (Stem*s)
 {
   stems_.push (s);
   s->add_dependency (this);
@@ -79,7 +78,7 @@ Beam::brew_molecule_p () const
       Molecule sb = stem_beams (i, next, prev);
       Real  x = i->hpos_f ()-x0;
       sb.translate (Offset (x, (x * slope_f_ + left_y_) * internote_f));
-      mol_p->add (sb);
+      mol_p->add_molecule (sb);
     }
   mol_p->translate_axis (x0 
     - spanned_drul_[LEFT]->absolute_coordinate (X_AXIS), X_AXIS);
@@ -105,7 +104,7 @@ void
 Beam::do_print () const
 {
 #ifndef NPRINT
-  DOUT << "slope_f_ " <<slope_f_ << "left ypos " << left_y_;
+  DOUT << "slope_f_ " << slope_f_ << "left ypos " << left_y_;
   Spanner::do_print ();
 #endif
 }
@@ -115,7 +114,7 @@ Beam::do_post_processing ()
 {
   if (stems_.size () < 2)
     {
-      warning (_ ("Beam with less than 2 stems"));
+      warning (_ ("beam with less than two stems"));
       transparent_b_ = true;
       return ;
     }
@@ -124,10 +123,10 @@ Beam::do_post_processing ()
 }
 
 void
-Beam::do_substitute_dependent (Score_elem*o,Score_elem*n)
+Beam::do_substitute_dependent (Score_element*o,Score_element*n)
 {
   if (o->is_type_b (Stem::static_name ()))
-      stems_.substitute ((Stem*)o->item (),  n? (Stem*) n->item ():0);
+      stems_.substitute ((Stem*)o->access_Item (),  n? (Stem*) n->access_Item ():0);
 }
 
 Interval
@@ -167,16 +166,43 @@ Beam::set_default_dir ()
   } while (flip(&d) != DOWN);
   
   /* 
-
      [Ross] states that the majority of the notes dictates the
      direction (and not the mean of "center distance")
+
+     But is that because it really looks better, or because he
+     wants to provide some real simple hands-on rules.
+     
+     We have our doubts, so we simply provide all sensible alternatives.
   */
-  dir_ = (total[UP] > total[DOWN]) ? UP : DOWN;
+
+  Dir_algorithm a = (Dir_algorithm)rint(paper ()->get_var ("beam_dir_algorithm"));
+  switch (a)
+    {
+    case MAJORITY:
+      dir_ = (count[UP] > count[DOWN]) ? UP : DOWN;
+      break;
+    case MEAN:
+      // mean centre distance
+      dir_ = (total[UP] > total[DOWN]) ? UP : DOWN;
+      break;
+    default:
+    case MEDIAN:
+      // median centre distance
+      if (!count[UP])
+       dir_ = DOWN;
+      else if (!count[DOWN])
+       dir_ = UP;
+      else
+       dir_ = (total[UP] / count[UP] > total[DOWN] / count[DOWN]) ? UP : DOWN;
+      break;
+    }
 
   for (int i=0; i <stems_.size (); i++)
     {
-      Stem *sl = stems_[i];
-      sl->dir_ = dir_;
+      Stem *s = stems_[i];
+      s->beam_dir_ = dir_;
+      if (!s->dir_forced_b_)
+       s->dir_ = dir_;
     }
 }
 
@@ -192,6 +218,7 @@ Beam::solve_slope ()
 
   assert (multiple_i_);
   Array<Stem_info> sinfo;
+  DOUT << "Beam::solve_slope: \n";
   for (int j=0; j <stems_.size (); j++)
     {
       Stem *i = stems_[j];
@@ -213,7 +240,6 @@ Beam::solve_slope ()
     }
   else
     {
-
       Real leftx = sinfo[0].x_;
       Least_squares l;
       for (int i=0; i < sinfo.size (); i++)
@@ -223,8 +249,57 @@ Beam::solve_slope ()
        }
 
       l.minimise (slope_f_, left_y_);
+
+     }
+
+  solved_slope_f_ = dir_ * slope_f_;
+
+  /*
+    This neat trick is by Werner Lemberg, damped = tanh (slope_f_) corresponds
+    with some tables in [Wanske]
+    */
+  if (damping_i_)
+    slope_f_ = 0.6 * tanh (slope_f_) / damping_i_;
+
+  /* 
+    [TODO]
+    think
+
+    dropping lq for stemlengths solves [d d d] [d g d] "bug..."
+
+    but may be a bit too crude, and result in lots of 
+    too high beams...
+
+    perhaps only if slope = 0 ?
+    */
+
+//      left_y_ = sinfo[0].minyf_;
+
+  if (sinfo.size () >= 1)
+    {
+      Real staffline_f = paper ()->rule_thickness ();
+      Real epsilon_f = staffline_f / 8;
+      if (abs (slope_f_) < epsilon_f)
+       left_y_ = (sinfo[0].idealy_f_ + sinfo.top ().idealy_f_) / 2;
+      else
+       /* 
+         symmetrical, but results often in having stemlength = minimal 
+
+       left_y_ = sinfo[0].dir_ == dir_ ? sinfo[0].miny_f_ : sinfo[0].maxy_f_;
+
+         what about
+       */
+       {
+         Real dx = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
+         if (sinfo[0].dir_ == sinfo.top ().dir_)
+           left_y_ = sinfo[0].idealy_f_ >? sinfo.top ().idealy_f_ - slope_f_ * dx; 
+         // knee
+         else
+           left_y_ = sinfo[0].idealy_f_;
+       }
     }
 
+  // uh?
   Real dy = 0.0;
   for (int i=0; i < sinfo.size (); i++)
     {
@@ -236,21 +311,9 @@ Beam::solve_slope ()
     }
   left_y_ += dy;
   left_y_ *= dir_;
-
   slope_f_ *= dir_;
 
-  /*
-    This neat trick is by Werner Lemberg, damped = tanh (slope_f_) corresponds
-    with some tables in [Wanske]
-    */
-  if (damping_i_)
-    slope_f_ = 0.6 * tanh (slope_f_) / damping_i_;
-
   quantise_dy ();
-
-  Real sl = slope_f_ * paper ()->internote_f ();
-  paper ()->lookup_l ()->beam (sl, 20 PT, 1 PT);
-  slope_f_ = sl / paper ()->internote_f ();
 }
 
 void
@@ -287,10 +350,10 @@ Beam::quantise_dy ()
   allowed_fraction[2] = (beam_f + staffline_f);
 
 
-    Interval iv = quantise_iv (allowed_fraction, interline_f, dy_f);
-    quanty_f = (dy_f - iv.min () <= iv.max () - dy_f)
-      ? iv.min ()
-      : iv.max ();
+  Interval iv = quantise_iv (allowed_fraction, interline_f, dy_f);
+  quanty_f = (dy_f - iv.min () <= iv.max () - dy_f)
+    ? iv.min ()
+    : iv.max ();
 
 
   slope_f_ = (quanty_f / dx_f) / internote_f * sign (slope_f_);
@@ -424,16 +487,57 @@ void
 Beam::set_stemlens ()
 {
   Real staffline_f = paper ()->rule_thickness ();
+  Real interbeam_f = paper ()->interbeam_f (multiple_i_);
+  Real internote_f = paper ()->internote_f (); 
+  Real beam_f = paper ()->beam_thickness_f ();
+
+  // enge floots
+  Real epsilon_f = staffline_f / 8;
 
+  /* 
+
+   Damped and quantised slopes, esp. in monotone scales such as
+
+      [c d e f g a b c]
+
+   will soon produce the minimal stem-length for one of the extreme 
+   stems, which is wrong (and ugly).  The minimum stemlength should
+   be kept rather small, in order to handle extreme beaming, such as
+
+      [c c' 'c]  %assuming no knee
+      
+   correctly.
+   To avoid these short stems for normal cases, we'll correct for
+   the loss in slope, if necessary.
+
+   [TODO]
+   ugh, another hack.  who's next?
+   Writing this all down, i realise (at last) that the Right Thing to
+   do is to assign uglyness to slope and stem-lengths and then minimise
+   the total uglyness of a beam.
+   Steep slopes are ugly, shortened stems are ugly, lengthened stems
+   are ugly.
+   How to do this?
+   
+   */
+
+  Real dx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
+  Real damp_correct_f = paper ()->get_var ("beam_slope_damp_correct_factor");
+  Real damped_slope_dy_f = (solved_slope_f_ - slope_f_) * dx_f
+    * sign (slope_f_);
+  damped_slope_dy_f *= damp_correct_f;
+  if (damped_slope_dy_f <= epsilon_f)
+    damped_slope_dy_f = 0;
+
+  DOUT << "Beam::set_stemlens: \n";
   Real x0 = stems_[0]->hpos_f ();
-  Real dy = 0;
-  // ugh, rounding problems! (enge floots)
-  Real epsilon = staffline_f / 8;
-  do
+  Real dy_f = 0;
+  // urg
+  for (int jj = 0; jj < 10; jj++)
     { 
-      left_y_ += dy * dir_;
-      quantise_left_y (dy);
-      dy = 0;
+      left_y_ += dy_f * dir_;
+      quantise_left_y (dy_f);
+      dy_f = 0;
       for (int i=0; i < stems_.size (); i++)
        {
          Stem *s = stems_[i];
@@ -441,13 +545,33 @@ Beam::set_stemlens ()
            continue;
 
          Real x = s->hpos_f () - x0;
-         s->set_stemend (left_y_ + slope_f_ * x);
+         // urg move this to stem-info
+         Real sy = left_y_ + slope_f_ * x;
+         if (dir_ != s->dir_)
+           sy -= dir_ * (beam_f / 2
+             + (s->mult_i_ - 1) * interbeam_f) / internote_f;
+         s->set_stemend (sy);
          Real y = s->stem_end_f () * dir_;
          Stem_info info (s);
+         if (y > info.maxy_f_)
+           dy_f = dy_f <? info.maxy_f_ - y;
          if (y < info.miny_f_)
-           dy = dy >? info.miny_f_ - y;
+           { 
+             // when all too short, normal stems win..
+             if (dy_f < -epsilon_f)
+               warning (_ ("weird beam shift, check your knees"));
+             dy_f = dy_f >? info.miny_f_ - y;
+           }
        }
-    } while (abs (dy) > epsilon);
+      if (damped_slope_dy_f && (dy_f >= 0))
+       dy_f += damped_slope_dy_f;
+      damped_slope_dy_f = 0;
+      if (abs (dy_f) <= epsilon_f)
+        {
+         DOUT << "Beam::set_stemlens: " << jj << " iterations\n";
+         break;
+       }
+    }
 
   test_pos++;
   test_pos %= 4;
@@ -511,7 +635,7 @@ Beam::stem_beams (Stem *here, Stem *next, Stem *prev) const
   Real dy = interbeam_f;
   Real stemdx = staffline_f;
   Real sl = slope_f_* internote_f;
-  paper ()->lookup_l ()->beam (sl, 20 PT, 1 PT);
+  lookup_l ()->beam (sl, 20 PT, 1 PT);
 
   Molecule leftbeams;
   Molecule rightbeams;
@@ -521,16 +645,21 @@ Beam::stem_beams (Stem *here, Stem *next, Stem *prev) const
     {
       int lhalfs= lhalfs = here->beams_left_i_ - prev->beams_right_i_ ;
       int lwholebeams= here->beams_left_i_ <? prev->beams_right_i_ ;
-      Real w = (here->hpos_f () - prev->hpos_f ())/4 <? paper ()->note_width ();;
+      /*
+       Half beam should be one note-width, 
+       but let's make sure two half-beams never touch
+       */
+      Real w = here->hpos_f () - prev->hpos_f ();
+      w = w/2 <? paper ()->note_width ();
       Atom a;
       if (lhalfs)              // generates warnings if not
-       a =  paper ()->lookup_l ()->beam (sl, w, beam_f);
+       a =  lookup_l ()->beam (sl, w, beam_f);
       a.translate (Offset (-w, -w * sl));
       for (int j = 0; j  < lhalfs; j++)
        {
          Atom b (a);
          b.translate_axis (-dir_ * dy * (lwholebeams+j), Y_AXIS);
-         leftbeams.add (b);
+         leftbeams.add_atom (b);
        }
     }
 
@@ -540,7 +669,7 @@ Beam::stem_beams (Stem *here, Stem *next, Stem *prev) const
       int rwholebeams = here->beams_right_i_ <? next->beams_left_i_;
 
       Real w = next->hpos_f () - here->hpos_f ();
-      Atom a = paper ()->lookup_l ()->beam (sl, w + stemdx, beam_f);
+      Atom a = lookup_l ()->beam (sl, w + stemdx, beam_f);
       a.translate_axis( - stemdx/2, X_AXIS);
       int j = 0;
       Real gap_f = 0;
@@ -551,34 +680,34 @@ Beam::stem_beams (Stem *here, Stem *next, Stem *prev) const
            {
              Atom b (a);
              b.translate_axis (-dir_ * dy * j, Y_AXIS);
-             rightbeams.add (b);
+             rightbeams.add_atom (b);
            }
          // TODO: notehead widths differ for different types
          gap_f = paper ()->note_width () / 2;
          w -= 2 * gap_f;
-         a = paper ()->lookup_l ()->beam (sl, w + stemdx, beam_f);
+         a = lookup_l ()->beam (sl, w + stemdx, beam_f);
        }
 
       for (; j  < rwholebeams; j++)
        {
          Atom b (a);
          b.translate (Offset (gap_f, -dir_ * dy * j));
-         rightbeams.add (b);
+         rightbeams.add_atom (b);
        }
 
-      w = w/4 <? paper ()->note_width ();
+      w = w/2 <? paper ()->note_width ();
       if (rhalfs)
-       a = paper ()->lookup_l ()->beam (sl, w, beam_f);
+       a = lookup_l ()->beam (sl, w, beam_f);
 
       for (; j  < rwholebeams + rhalfs; j++)
        {
          Atom b (a);
          b.translate_axis (-dir_ * dy * j, Y_AXIS);
-         rightbeams.add (b);
+         rightbeams.add_atom (b);
        }
 
     }
-  leftbeams.add (rightbeams);
+  leftbeams.add_molecule (rightbeams);
 
   /*
     Does beam quanting think  of the asymetry of beams?