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--1999, 1998 Han-Wen Nienhuys <hanwen@cs.uu.nl>
+ Jan Nieuwenhuizen <janneke@gnu.org>
*/
#include <math.h>
#include "p-col.hh"
-#include "varray.hh"
+#include "array.hh"
#include "proto.hh"
-#include "dimen.hh"
+#include "dimensions.hh"
#include "beam.hh"
#include "abbreviation-beam.hh"
#include "misc.hh"
#include "debug.hh"
-#include "atom.hh"
+
#include "molecule.hh"
#include "leastsquares.hh"
#include "stem.hh"
#include "paper-def.hh"
#include "lookup.hh"
-#include "grouping.hh"
-#include "stem-info.hh"
-#include "main.hh" // experimental features
-
-
-IMPLEMENT_IS_TYPE_B1 (Beam, Spanner);
+#include "rhythmic-grouping.hh"
Beam::Beam ()
{
slope_f_ = 0;
- solved_slope_f_ = 0;
left_y_ = 0;
damping_i_ = 1;
quantisation_ = NORMAL;
multiple_i_ = 0;
+ vertical_align_drul_[MIN] = 0;
+ vertical_align_drul_[MAX] = -1;
}
void
-Beam::add (Stem*s)
+Beam::add_stem (Stem*s)
{
stems_.push (s);
s->add_dependency (this);
}
Molecule*
-Beam::brew_molecule_p () const
+Beam::do_brew_molecule_p () const
{
Molecule *mol_p = new Molecule;
-
Real internote_f = paper ()->internote_f ();
Real x0 = stems_[0]->hpos_f ();
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);
+ // correct if last note (and therefore reference point of beam)
+ // is on different staff
+ mol_p->translate_axis (- sinfo_.top ().interstaff_f_ * internote_f, Y_AXIS);
+
return mol_p;
}
Offset
Beam::center () const
{
- Real w= (paper ()->note_width () + width ().length ())/2.0;
+ Real w= (paper ()->note_width () + extent (X_AXIS).length ())/2.0;
return Offset (w, (left_y_ + w* slope_f_)*paper ()->internote_f ());
}
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
}
{
if (stems_.size () < 2)
{
- warning (_ ("Beam with less than 2 stems"));
+ warning (_ ("beam with less than two stems"));
transparent_b_ = true;
return ;
}
- solve_slope ();
+ calculate_slope ();
set_stemlens ();
}
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);
+ if (Stem * os = dynamic_cast<Stem*> (o))
+ stems_.substitute (os,
+ dynamic_cast<Stem *> (n));
}
Interval
} while (flip(&d) != DOWN);
- do {
- if (!total[d])
- count[d] = 1;
- } 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.
+ We have our doubts, so we simply provide all sensible alternatives.
*/
- // fixme. make runtime.
- // majority
- // dir_ = (count[UP] > count[DOWN]) ? UP : DOWN;
-
- // mean centre distance
- 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++)
{
/*
See Documentation/tex/fonts.doc
*/
+
void
Beam::solve_slope ()
{
/*
should use minimum energy formulation (cf linespacing)
*/
+ assert (sinfo_.size () > 1);
+ DOUT << "Beam::solve_slope: \n";
+
+ Least_squares l;
+ for (int i=0; i < sinfo_.size (); i++)
+ {
+ l.input.push (Offset (sinfo_[i].x_, sinfo_[i].idealy_f_));
+ }
+ l.minimise (slope_f_, left_y_);
+}
+
+Real
+Beam::check_stemlengths_f (bool set_b)
+{
+ Real interbeam_f = paper ()->interbeam_f (multiple_i_);
+ Real internote_f = paper ()->internote_f ();
+ Real beam_f = paper ()->beam_thickness_f ();
+ Real staffline_f = paper ()->rule_thickness ();
+ Real epsilon_f = staffline_f / 8;
+ Real dy_f = 0.0;
+ for (int i=0; i < sinfo_.size (); i++)
+ {
+ Real y = sinfo_[i].x_ * slope_f_ + left_y_;
+ // correct for knee
+ if (dir_ != sinfo_[i].dir_)
+ {
+ y -= dir_ * (beam_f / 2
+ + (sinfo_[i].mult_i_ - 1) * interbeam_f) / internote_f;
+ if (!i && sinfo_[i].stem_l_->staff_sym_l_ !=
+ sinfo_.top ().stem_l_->staff_sym_l_)
+ y += dir_ * (multiple_i_ - (sinfo_[i].stem_l_->flag_i_ - 2) >? 0)
+ * interbeam_f / internote_f;
+ }
+
+ if (set_b)
+ sinfo_[i].stem_l_->set_stemend (y - sinfo_[i].interstaff_f_);
+
+ y *= dir_;
+ if (y > sinfo_[i].maxy_f_)
+ dy_f = dy_f <? sinfo_[i].maxy_f_ - y;
+ if (y < sinfo_[i].miny_f_)
+ {
+ // when all too short, normal stems win..
+ if (dy_f < -epsilon_f)
+ warning (_ ("weird beam shift, check your knees"));
+ dy_f = dy_f >? sinfo_[i].miny_f_ - y;
+ }
+ }
+ return dy_f;
+}
+
+void
+Beam::set_steminfo ()
+{
assert (multiple_i_);
- Array<Stem_info> sinfo;
- DOUT << "Beam::solve_slope: \n";
- for (int j=0; j <stems_.size (); j++)
+ int total_count_i = 0;
+ int forced_count_i = 0;
+ for (int i=0; i < stems_.size (); i++)
{
- Stem *i = stems_[j];
+ Stem *s = stems_[i];
+ s->mult_i_ = multiple_i_;
+ s->set_default_extents ();
+ if (s->invisible_b ())
+ continue;
+ if (((int)s->chord_start_f ()) && (s->dir_ != s->get_default_dir ()))
+ forced_count_i++;
+ total_count_i++;
+ }
- i->mult_i_ = multiple_i_;
- i->set_default_extents ();
- if (i->invisible_b ())
+ Real internote_f = paper ()->internote_f ();
+ int stem_max = (int)rint(paper ()->get_var ("stem_max"));
+ Real shorten_f = paper ()->get_var (String ("forced_stem_shorten"
+ + to_str (multiple_i_ <? stem_max)))
+ / internote_f;
+
+ Real leftx = 0;
+ for (int i=0; i < stems_.size (); i++)
+ {
+ Stem *s = stems_[i];
+ if (s->invisible_b ())
continue;
- Stem_info info (i);
- sinfo.push (info);
+ Stem_info info (s);
+ if (leftx == 0)
+ leftx = info.x_;
+ info.x_ -= leftx;
+ if (info.dir_ == dir_)
+ {
+ if (forced_count_i == total_count_i)
+ info.idealy_f_ -= shorten_f;
+ else if (forced_count_i > total_count_i / 2)
+ info.idealy_f_ -= shorten_f / 2;
+ }
+ sinfo_.push (info);
}
- if (! sinfo.size ())
+}
+
+void
+Beam::calculate_slope ()
+{
+ set_steminfo ();
+ if (!sinfo_.size ())
slope_f_ = left_y_ = 0;
- else if (sinfo.size () == 1)
+ else if (sinfo_[0].idealy_f_ == sinfo_.top ().idealy_f_)
{
slope_f_ = 0;
- left_y_ = sinfo[0].idealy_f_;
+ left_y_ = sinfo_[0].idealy_f_;
+ left_y_ *= dir_;
}
else
{
- Real leftx = sinfo[0].x_;
- Least_squares l;
- for (int i=0; i < sinfo.size (); i++)
+ solve_slope ();
+ Real solved_slope_f = slope_f_;
+
+ /*
+ steep slope running against lengthened stem is suspect
+ */
+ Real dx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
+
+ // urg, these y internote-y-dimensions
+ Real internote_f = paper ()->internote_f ();
+ Real lengthened = paper ()->get_var ("beam_lengthened") / internote_f;
+ Real steep = paper ()->get_var ("beam_steep_slope") / internote_f;
+ if (((left_y_ - sinfo_[0].idealy_f_ > lengthened)
+ && (slope_f_ > steep))
+ || ((left_y_ + slope_f_ * dx_f - sinfo_.top ().idealy_f_ > lengthened)
+ && (slope_f_ < -steep)))
{
- sinfo[i].x_ -= leftx;
- l.input.push (Offset (sinfo[i].x_, sinfo[i].idealy_f_));
+ slope_f_ = 0;
}
- 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 ();
- left_y_ = sinfo[0].idealy_f_ >? sinfo.top ().idealy_f_ - slope_f_ * dx;
- }
- }
+ /*
+ 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 ();
- // uh?
- Real dy = 0.0;
- for (int i=0; i < sinfo.size (); i++)
- {
- Real y = sinfo[i].x_ * slope_f_ + left_y_;
- Real my = sinfo[i].miny_f_;
+ Real damped_slope_dy_f = (solved_slope_f - slope_f_) * dx_f / 2;
+ left_y_ += damped_slope_dy_f;
- if (my - y > dy)
- dy = my -y;
+ left_y_ *= dir_;
+ slope_f_ *= dir_;
}
- left_y_ += dy;
- left_y_ *= dir_;
- slope_f_ *= dir_;
-
- quantise_dy ();
}
void
if (test_pos == 0)
{
allowed_position.push (hang);
- cout << "hang" << hang << endl;
+ cout << "hang" << hang << "\n";
}
else if (test_pos==1)
{
}
}
-#if 0
- // this currently never happens
- Real q = (dy_f / interline_f - dy_i) * interline_f;
- if ((quantisation_ < NORMAL) && (q < interline_f / 3 - beam_f / 2))
- allowed_position.push (inter);
-#endif
-
Interval iv = quantise_iv (allowed_position, interline_f, dy_f);
Real quanty_f = dy_f - iv.min () <= iv.max () - dy_f ? iv.min () : iv.max ();
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_f = 0;
- // urg
- for (int jj = 0; jj < 10; jj++)
+ Real dy_f = check_stemlengths_f (false);
+ for (int i = 0; i < 2; i++)
{
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];
- if (s->transparent_b_)
- continue;
-
- Real x = s->hpos_f () - x0;
- // 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_)
- {
- // 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;
- }
- }
- if (damped_slope_dy_f && (dy_f >= 0))
- dy_f += damped_slope_dy_f;
- damped_slope_dy_f = 0;
+ dy_f = check_stemlengths_f (true);
if (abs (dy_f) <= epsilon_f)
{
- DOUT << "Beam::set_stemlens: " << jj << " iterations\n";
+ DOUT << "Beam::set_stemlens: " << i << " iterations\n";
break;
}
}
assert (stems_.size () == b.size ()/2);
}
- for (int j=0, i=0; i < b.size () && j <stems_.size (); i+= 2, j++)
+ for (int j=0, i=0; i < b.size () && j <stems_.size (); j++)
{
Stem *s = stems_[j];
- s->beams_left_i_ = b[i];
- s->beams_right_i_ = b[i+1];
- multiple_i_ = multiple_i_ >? (b[i] >? b[i+1]);
+ Direction d = LEFT;
+ do {
+ if (s->beams_i_drul_[d] < 0)
+ s->beams_i_drul_[d] = b[i];
+
+ multiple_i_ = multiple_i_ >? s->beams_i_drul_[d];
+ i++;
+ } while ((flip (&d)) != LEFT);
}
}
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;
+ // UGH
+ Real nw_f = paper ()->note_width () * 0.8;
+
/* half beams extending to the left. */
if (prev)
{
- 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 ();;
- Atom a;
+ int lhalfs= lhalfs = here->beams_i_drul_[LEFT] - prev->beams_i_drul_[RIGHT] ;
+ int lwholebeams= here->beams_i_drul_[LEFT] <? prev->beams_i_drul_[RIGHT] ;
+ /*
+ 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 <? nw_f;
+ Molecule 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);
+ Molecule b (a);
b.translate_axis (-dir_ * dy * (lwholebeams+j), Y_AXIS);
- leftbeams.add (b);
+ leftbeams.add_molecule (b);
}
}
if (next)
{
- int rhalfs = here->beams_right_i_ - next->beams_left_i_;
- int rwholebeams = here->beams_right_i_ <? next->beams_left_i_;
+ int rhalfs = here->beams_i_drul_[RIGHT] - next->beams_i_drul_[LEFT];
+ int rwholebeams = here->beams_i_drul_[RIGHT] <? next->beams_i_drul_[LEFT];
Real w = next->hpos_f () - here->hpos_f ();
- Atom a = paper ()->lookup_l ()->beam (sl, w + stemdx, beam_f);
+ Molecule a = lookup_l ()->beam (sl, w + stemdx, beam_f);
a.translate_axis( - stemdx/2, X_AXIS);
int j = 0;
Real gap_f = 0;
int nogap = rwholebeams - here->beam_gap_i_;
for (; j < nogap; j++)
{
- Atom b (a);
+ Molecule b (a);
b.translate_axis (-dir_ * dy * j, Y_AXIS);
- rightbeams.add (b);
+ rightbeams.add_molecule (b);
}
// TODO: notehead widths differ for different types
- gap_f = paper ()->note_width () / 2;
+ gap_f = nw_f / 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);
+ Molecule b (a);
b.translate (Offset (gap_f, -dir_ * dy * j));
- rightbeams.add (b);
+ rightbeams.add_molecule (b);
}
- w = w/4 <? paper ()->note_width ();
+ w = w/2 <? nw_f;
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);
+ Molecule b (a);
b.translate_axis (-dir_ * dy * j, Y_AXIS);
- rightbeams.add (b);
+ rightbeams.add_molecule (b);
}
}
- leftbeams.add (rightbeams);
+ leftbeams.add_molecule (rightbeams);
/*
Does beam quanting think of the asymetry of beams?