]> git.donarmstrong.com Git - lilypond.git/blobdiff - lily/beam.cc
* scm/font.scm (paper20-font-vector): add bold-narrow series (only
[lilypond.git] / lily / beam.cc
index c675c0c05276330e151c6514d5676e25be5260f2..5361db25b50bd17f2dc988eb25bb67c6c3e8e40e 100644 (file)
@@ -3,7 +3,7 @@
   
   source file of the GNU LilyPond music typesetter
   
-  (c)  1997--2002 Han-Wen Nienhuys <hanwen@cs.uu.nl>
+  (c)  1997--2003 Han-Wen Nienhuys <hanwen@cs.uu.nl>
   Jan Nieuwenhuizen <janneke@gnu.org>
 */
 
@@ -14,6 +14,7 @@ TODO:
   
   - Determine auto knees based on positions if it's set by the user.
 
+  - rounded corners.
 
 Notes:
 
@@ -43,7 +44,8 @@ Notes:
 #include "warn.hh"
 
 
-#define DEBUG_QUANTING 0
+
+bool debug_beam_quanting_flag;
 
 
 #if DEBUG_QUANTING
@@ -98,6 +100,10 @@ Beam::get_beam_count (Grob *me)
   return m;
 }
 
+
+/*
+  Space return space between beams.
+ */
 MAKE_SCHEME_CALLBACK (Beam, space_function, 2);
 SCM
 Beam::space_function (SCM smob, SCM beam_count)
@@ -105,7 +111,7 @@ Beam::space_function (SCM smob, SCM beam_count)
   Grob *me = unsmob_grob (smob);
   
   Real staff_space = Staff_symbol_referencer::staff_space (me);
-  Real line = me->get_paper ()->get_var ("linethickness");
+  Real line = me->get_paper ()->get_realvar (ly_symbol2scm ("linethickness"));
   Real thickness = get_thickness (me);
   
   Real beam_translation = gh_scm2int (beam_count) < 4
@@ -232,7 +238,7 @@ Beam::connect_beams (Grob *me)
       Grob *this_stem = stems[i];
       SCM this_beaming = this_stem->get_grob_property ("beaming");
 
-      Direction this_dir = Directional_element_interface::get(this_stem);
+      Direction this_dir = get_grob_direction (this_stem);
       if (gh_pair_p (last_beaming) && gh_pair_p (this_beaming))
        {
          int start_point = position_with_maximal_common_beams
@@ -261,7 +267,7 @@ Beam::connect_beams (Grob *me)
            }
          while (flip (&d) != LEFT);
 
-         if (!new_slice.empty_b())
+         if (!new_slice.is_empty ())
            last_int =  new_slice;
        }
       else
@@ -313,7 +319,7 @@ Beam::brew_molecule (SCM grob)
 
   SCM posns = me->get_grob_property ("positions");
   Interval pos;
-  if (!ly_number_pair_p (posns))
+  if (!is_number_pair (posns))
     {
       programming_error ("No beam posns");
       pos = Interval (0,0);
@@ -322,7 +328,7 @@ Beam::brew_molecule (SCM grob)
     pos= ly_scm2interval (posns);
 
   Real dy = pos.delta ();
-  Real dydx = dy && dx ? dy/dx : 0;
+  Real dydx = (dy && dx) ? dy/dx : 0;
   
   Real thick = get_thickness (me);
   Real bdy = get_beam_translation (me);
@@ -331,121 +337,145 @@ Beam::brew_molecule (SCM grob)
   Real last_xposn = -1;
   Real last_width = -1 ;
 
-
-  SCM gap = me->get_grob_property ("gap");
+  Real gap_length =0.0;
+  SCM scm_gap = me->get_grob_property ("gap");
+  if (gh_number_p (scm_gap))
+    gap_length = gh_scm2double (scm_gap);
+  
   Molecule the_beam;
-  Real lt = me->get_paper ()->get_var ("linethickness");
+  Real lt = me->get_paper ()->get_realvar (ly_symbol2scm ("linethickness"));
   
-  for (int i = 0; i< stems.size(); i++)
+  for (int i = 0; i<= stems.size(); i++)
     {
-      Grob * st =stems[i];
+      Grob * st = (i < stems.size()) ? stems[i] : 0;
       
-      SCM this_beaming = st->get_grob_property ("beaming");
-      Real xposn = st->relative_coordinate (xcommon, X_AXIS);
-      Real stem_width = gh_scm2double (st->get_grob_property ("thickness")) *lt;
-
+      SCM this_beaming = st ? st->get_grob_property ("beaming") : SCM_EOL;
+      Real xposn = st ? st->relative_coordinate (xcommon, X_AXIS) : 0.0;
+      Real stem_width = st ? gh_scm2double (st->get_grob_property ("thickness")) *lt : 0 ;
+      Direction stem_dir = st ? to_dir (st->get_grob_property ("direction")) : CENTER;
       /*
        We do the space left of ST, with lfliebertjes pointing to the
        right from the left stem, and rfliebertjes pointing left from
        right stem.
        */
-      if (i > 0)
-       {
-         SCM left = gh_cdr (last_beaming);
-         SCM right = gh_car (this_beaming);
+      SCM left = (i>0) ? gh_cdr (last_beaming) : SCM_EOL;
+      SCM right = st ? gh_car (this_beaming) : SCM_EOL;
 
-         Array<int> fullbeams;
-         Array<int> lfliebertjes;
-         Array<int> rfliebertjes;        
+      Array<int> full_beams;
+      Array<int> lfliebertjes;
+      Array<int> rfliebertjes;   
 
-         for (SCM s = left;
-              gh_pair_p (s); s =gh_cdr (s))
+      for (SCM s = left;
+          gh_pair_p (s); s =gh_cdr (s))
+       {
+         int b = gh_scm2int (gh_car (s));
+         if (scm_memq (gh_car(s), right) != SCM_BOOL_F)
            {
-             int b = gh_scm2int (gh_car (s));
-             if (scm_memq (gh_car(s), right) != SCM_BOOL_F)
-               {
-                 fullbeams.push (b);
-               }
-             else
-               {
-                 lfliebertjes.push (b); 
-               }
+             full_beams.push (b);
            }
-         for (SCM s = right;
-              gh_pair_p (s); s =gh_cdr (s))
+         else
            {
-             int b = gh_scm2int (gh_car (s));
-             if (scm_memq (gh_car(s), left) == SCM_BOOL_F)
-               {
-                 rfliebertjes.push (b);
-               }
+             lfliebertjes.push (b); 
            }
-
-         
-         Real w = xposn - last_xposn;
-         Real stem_offset = 0.0;
-         Real width_corr = 0.0;
-         if (i == 1)
+       }
+      for (SCM s = right;
+          gh_pair_p (s); s =gh_cdr (s))
+       {
+         int b = gh_scm2int (gh_car (s));
+         if (scm_memq (gh_car(s), left) == SCM_BOOL_F)
            {
-             stem_offset -= last_width/2;
-             width_corr += last_width/2;
+             rfliebertjes.push (b);
            }
+       }
+
+      /*
+       how much to stick out for beams across linebreaks
+       */
+      Real break_overshoot = 3.0;
+      Real w = (i>0 && st)? xposn - last_xposn : break_overshoot;
+      Real stem_offset = 0.0;
+      Real width_corr = 0.0;
+      if (i == 1)
+       {
+         stem_offset -= last_width/2;
+         width_corr += last_width/2;
+       }
          
-         if (i == stems.size() -1)
-           {
-             width_corr += stem_width/2;
-           }
+      if (i == stems.size() -1)
+       {
+         width_corr += stem_width/2;
+       }
 
-         if (gh_number_p (gap))
-           {
-             Real g = gh_scm2double (gap);
-             stem_offset += g;
-             width_corr -= 2*g; 
-           }
          
-         Molecule whole = Lookup::beam (dydx, w + width_corr, thick);
-         for (int j = fullbeams.size(); j--;)
+      Molecule whole = Lookup::beam (dydx, w + width_corr, thick);
+      Molecule gapped;
+
+      int gap_count = 0;
+      if (gh_number_p (me->get_grob_property ("gap-count")))
+       {
+         gap_count = gh_scm2int (me->get_grob_property ("gap-count"));
+         gapped = Lookup::beam (dydx, w + width_corr - 2 * gap_length, thick);
+
+         full_beams.sort (default_compare);
+         if (stem_dir == UP)
+           full_beams.reverse ();
+       }
+
+      int k = 0;
+      for (int j = full_beams.size (); j--;)
+       {
+         Molecule b (whole);
+         
+         if (k++ < gap_count)
            {
-             Molecule b (whole);
-             b.translate_axis (last_xposn -  x0 + stem_offset, X_AXIS);
-             b.translate_axis (dydx * (last_xposn - x0) + bdy * fullbeams[j], Y_AXIS);
-             the_beam.add_molecule (b);              
+             b = gapped;
+             b.translate_axis (gap_length, X_AXIS);
            }
+         b.translate_axis (last_xposn -  x0 + stem_offset, X_AXIS);
+         b.translate_axis (dydx * (last_xposn - x0) + bdy * full_beams[j], Y_AXIS);
 
-         if (lfliebertjes.size() || rfliebertjes.size())
-           {
-             Real nw_f;
+         the_beam.add_molecule (b);          
+       }
 
+      
+         
+      if (lfliebertjes.size() || rfliebertjes.size())
+       {
+         Real nw_f;
+
+         if (st)
+           {
              int t = Stem::duration_log (st); 
 
              SCM proc = me->get_grob_property ("flag-width-function");
              SCM result = gh_call1 (proc, scm_int2num (t));
              nw_f = gh_scm2double (result);
-               
-             
-             /* Half beam should be one note-width,
-                but let's make sure two half-beams never touch */
+           }
+         else
+           nw_f = break_overshoot;
              
-             Real w = xposn - last_xposn;
-             w = w/2 <? nw_f;
+         /* Half beam should be one note-width,
+            but let's make sure two half-beams never touch */
+         Real w = (i>0 && st) ? (xposn - last_xposn) : break_overshoot;
+         w = w/2 <? nw_f;
 
-             Molecule half = Lookup::beam (dydx, w, thick);
-             for (int j = lfliebertjes.size(); j--;)
-               {
-                 Molecule b (half);
-                 b.translate_axis (last_xposn -  x0, X_AXIS);
-                 b.translate_axis (dydx * (last_xposn-x0) + bdy * lfliebertjes[j], Y_AXIS);
-                 the_beam.add_molecule (b);          
-               }
-             for (int j = rfliebertjes.size(); j--;)
-               {
-                 Molecule b (half);
-                 b.translate_axis (xposn -  x0 - w , X_AXIS);
-                 b.translate_axis (dydx * (xposn-x0 -w) + bdy * rfliebertjes[j], Y_AXIS);
-                 the_beam.add_molecule (b);          
-               }
+         Molecule half = Lookup::beam (dydx, w, thick);
+         for (int j = lfliebertjes.size(); j--;)
+           {
+             Molecule b (half);
+             b.translate_axis (last_xposn -  x0, X_AXIS);
+             b.translate_axis (dydx * (last_xposn-x0) + bdy * lfliebertjes[j], Y_AXIS);
+             the_beam.add_molecule (b);              
            }
-       }
+         for (int j = rfliebertjes.size(); j--;)
+           {
+             Molecule b (half);
+             b.translate_axis (xposn -  x0 - w , X_AXIS);
+             b.translate_axis (dydx * (xposn-x0 -w) + bdy * rfliebertjes[j], Y_AXIS);
+             the_beam.add_molecule (b);              
+           }
+       }
+
 
       last_xposn = xposn;
       last_width = stem_width;
@@ -456,25 +486,21 @@ Beam::brew_molecule (SCM grob)
   the_beam.translate_axis (pos[LEFT], Y_AXIS);
 
 #if (DEBUG_QUANTING)
+  SCM quant_score = me->get_grob_property ("quant-score");
+  if (debug_beam_quanting_flag
+      && gh_string_p (quant_score))
     {
+      
       /*
        This code prints the demerits for each beam. Perhaps this
        should be switchable for those who want to twiddle with the
        parameters.
       */
       String str;
-      if (1)
-       {
-         str += to_string (gh_scm2int (me->get_grob_property ("best-idx")));
-         str += ":";
-       }
-      str += to_string (gh_scm2double (me->get_grob_property ("quant-score")),
-                    "%.2f");
-
       SCM properties = Font_interface::font_alist_chain (me);
 
-      
-      Molecule tm = Text_item::text2molecule (me, scm_makfrom0str (str.to_str0 ()), properties);
+      Molecule tm = *unsmob_molecule (Text_item::interpret_markup
+       (me->get_paper ()->self_scm (), properties, quant_score));
       the_beam.add_at_edge (Y_AXIS, UP, tm, 5.0, 0);
     }
 #endif
@@ -502,7 +528,7 @@ Beam::get_default_dir (Grob *me)
   for (int i=0; i <stems.size (); i++)
     do {
       Grob *s = stems[i];
-      Direction sd = Directional_element_interface::get (s);
+      Direction sd = get_grob_direction (s);
 
       int center_distance = int(- d * Stem::head_positions (s) [-d]) >? 0;
       int current = sd ? (1 + d * sd)/2 : center_distance;
@@ -544,7 +570,7 @@ Beam::set_stem_directions (Grob *me, Direction d)
   
       SCM forcedir = s->get_grob_property ("direction");
       if (!to_dir (forcedir))
-       Directional_element_interface::set (s, d);
+       set_grob_direction (s,  d);
     }
 }
 
@@ -580,7 +606,7 @@ struct Int_set
 
        s.intersect (allowed_regions_[i]);
 
-       if (!s.empty_b ())
+       if (!s.is_empty ())
          {
            Interval before = allowed_regions_[i];
            Interval after = allowed_regions_[i];
@@ -588,13 +614,13 @@ struct Int_set
            before[RIGHT] = s[LEFT];
            after[LEFT] = s[RIGHT];
 
-           if (!before.empty_b() && before.length () > 0.0)
+           if (!before.is_empty () && before.length () > 0.0)
              {
                allowed_regions_.insert (before, i);
                i++;
              }
            allowed_regions_.del (i);
-           if (!after.empty_b () && after.length () > 0.0)
+           if (!after.is_empty () && after.length () > 0.0)
              {
                allowed_regions_.insert (after, i);
                i++;
@@ -640,7 +666,7 @@ Beam::consider_auto_knees (Grob* me)
        continue;
 
       Interval hps = Stem::head_positions (stem);
-      if(!hps.empty_b())
+      if(!hps.is_empty ())
        {
          hps[LEFT] += -1;
          hps[RIGHT] += 1; 
@@ -703,7 +729,7 @@ Beam::consider_auto_knees (Grob* me)
          stem->set_grob_property ("direction", scm_int2num (d));
          
          hps.intersect (max_gap);
-         assert (hps.empty_b () || hps.length () < 1e-6 );
+         assert (hps.is_empty () || hps.length () < 1e-6 );
        }
     }
 }
@@ -778,6 +804,10 @@ Beam::after_line_breaking (SCM smob)
   return SCM_UNSPECIFIED;
 }
 
+
+/*
+  Compute  a first approximation to the beam slope.
+ */
 MAKE_SCHEME_CALLBACK (Beam, least_squares, 1);
 SCM
 Beam::least_squares (SCM smob)
@@ -787,7 +817,7 @@ Beam::least_squares (SCM smob)
   int count = visible_stem_count (me);
   Interval pos (0, 0);
   
-  if (count <= 1)
+  if (count < 1)
     {
       me->set_grob_property ("positions", ly_interval2scm (pos));
       return SCM_UNSPECIFIED;
@@ -820,6 +850,7 @@ Beam::least_squares (SCM smob)
     }
   Real dx = last_visible_stem (me)->relative_coordinate (commonx, X_AXIS) - x0;
 
+  
   Real y =0;  
   Real dydx = 0;
   Real dy = 0;
@@ -848,9 +879,18 @@ Beam::least_squares (SCM smob)
          pos = ideal;
        }
 
+      /*
+       For broken beams this doesn't work well. In this case, the
+        slope esp. of the first part of a broken beam should predict
+        where the second part goes.
+       */
+
       y = pos[LEFT];
       dy = pos[RIGHT]- y;
       dydx = dy/dx;
+
+
+
     }
   else
     {
@@ -865,6 +905,7 @@ Beam::least_squares (SCM smob)
                               + s->relative_coordinate (commony, Y_AXIS)
                               - my_y));
        }
+      
       minimise_least_squares (&dydx, &y, ideals);
 
       dy = dydx * dx;
@@ -881,6 +922,10 @@ Beam::least_squares (SCM smob)
 /*
   We can't combine with previous function, since check concave and
   slope damping comes first.
+
+TODO: we should use the concaveness to control the amount of damping
+applied.
+  
  */
 MAKE_SCHEME_CALLBACK (Beam, shift_region_to_valid, 1);
 SCM
@@ -955,7 +1000,7 @@ Beam::shift_region_to_valid (SCM grob)
       feasible_left_point.intersect (flp);
     }
       
-  if (feasible_left_point.empty_b())
+  if (feasible_left_point.is_empty ())
     {
       warning (_("Not sure that we can find a nice beam slope (no viable initial configuration found)."));
     }
@@ -1068,9 +1113,13 @@ Beam::check_concave (SCM smob)
       concave *= dir;
       concaveness2 = concave / (stems.size () - 2);
       
-      /* ugh: this is the a kludge to get
-        input/regression/beam-concave.ly to behave as
-        baerenreiter. */
+      /*
+
+      ugh: this is the a kludge to get
+      input/regression/beam-concave.ly to behave as
+      baerenreiter.
+
+      */
 
       /*
        huh? we're dividing twice (which is not scalable) meaning that
@@ -1172,20 +1221,20 @@ Beam::calc_stem_y (Grob *me, Grob* s, Grob ** common,
                       * dy
                       : 0) + pos[LEFT];
   
-  Direction my_dir = Directional_element_interface::get (s);
+  Direction my_dir = get_grob_direction (s);
   SCM beaming = s->get_grob_property ("beaming");
  
   Real stem_y = stem_y_beam0;
   if (french)
     {
       Slice bm = where_are_the_whole_beams (beaming);
-      if (!bm.empty_b())
+      if (!bm.is_empty ())
        stem_y += beam_translation * bm[-my_dir];
     }
   else
     {
       Slice bm = Stem::beam_multiplicity(s);
-      if (!bm.empty_b())
+      if (!bm.is_empty ())
        stem_y +=bm[my_dir] * beam_translation;
     }
   
@@ -1205,7 +1254,7 @@ Beam::set_stem_lengths (Grob *me)
   Link_array<Grob> stems=
     Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
 
-  if (stems.size () <= 1)
+  if (!stems.size ())
     return;
   
   Grob *common[2];
@@ -1215,17 +1264,14 @@ Beam::set_stem_lengths (Grob *me)
   Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
   Real staff_space = Staff_symbol_referencer::staff_space (me);
 
-  bool french = to_boolean (me->get_grob_property ("french-beaming"));
-
-  
   bool gap = false;
   Real thick =0.0;
-  if (gh_number_p (me->get_grob_property ("gap"))
-      &&gh_scm2double (me->get_grob_property ("gap")))
-  {
-    gap = true;
-    thick = get_thickness(me);
-  }
+  if (gh_number_p (me->get_grob_property ("gap-count"))
+      &&gh_scm2int (me->get_grob_property ("gap-count")))
+    {
+      gap = true;
+      thick = get_thickness(me);
+    }
       
   // ugh -> use commonx
   Grob * fvs = first_visible_stem (me);
@@ -1240,17 +1286,18 @@ Beam::set_stem_lengths (Grob *me)
       if (Stem::invisible_b (s))
        continue;
 
+      bool french = to_boolean (s->get_grob_property ("french-beaming"));
       Real stem_y = calc_stem_y (me, s, common,
                                 xl, xr,
-                                pos, french && i > 0&& (i < stems.size  () -1));
+                                pos, french && s != lvs && s!= fvs);
 
       /*
        Make the stems go up to the end of the beam. This doesn't matter
        for normal beams, but for tremolo beams it looks silly otherwise.
        */
       if (gap)
-       stem_y += thick * 0.5 * Directional_element_interface::get(s);
-      
+       stem_y += thick * 0.5 * get_grob_direction (s);
+
       Stem::set_stemend (s, 2* stem_y / staff_space);
     }
 }
@@ -1455,8 +1502,7 @@ Beam::knee_b (Grob* me)
   int d = 0;
   for (SCM s = me->get_grob_property ("stems"); gh_pair_p (s); s = ly_cdr (s))
     {
-      Direction dir = Directional_element_interface::get
-       (unsmob_grob (ly_car (s)));
+      Direction dir = get_grob_direction (unsmob_grob (ly_car (s)));
       if (d && d != dir)
        {
          knee = true;
@@ -1507,6 +1553,6 @@ ADD_INTERFACE (Beam, "beam-interface",
 "the ideal slope, how close the result is to the ideal stems, etc.). We "
 "take the best scoring combination. "
 ,
-  "knee french-beaming position-callbacks concaveness-gap concaveness-threshold dir-function quant-score auto-knee-gap gap chord-tremolo beamed-stem-shorten shorten least-squares-dy damping flag-width-function neutral-direction positions space-function thickness");
+  "knee position-callbacks concaveness-gap concaveness-threshold dir-function quant-score auto-knee-gap gap gap-count chord-tremolo beamed-stem-shorten shorten least-squares-dy damping flag-width-function neutral-direction positions space-function thickness");