X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=lily%2Fbeam.cc;h=0f9a9817200d3215a6e471bff30af3d1900400fe;hb=7bfd28ec3876ca0adff5fe9e574ade748a3191af;hp=a20e859e23a03cefda2226205432aa672d5e7553;hpb=3f374e66968308461daa390a7aeed0b013bfe33d;p=lilypond.git diff --git a/lily/beam.cc b/lily/beam.cc index a20e859e23..0f9a981720 100644 --- a/lily/beam.cc +++ b/lily/beam.cc @@ -1,298 +1,554 @@ -#include "varray.hh" +/* + beam.cc -- implement Beam + + source file of the GNU LilyPond music typesetter + + (c) 1997 Han-Wen Nienhuys + + TODO + + Less hairy code. knee: ([\stem 1; c8 \stem -1; c8] +*/ + +#include + +#include "p-col.hh" +#include "varray.hh" #include "proto.hh" #include "dimen.hh" #include "beam.hh" +#include "abbreviation-beam.hh" #include "misc.hh" #include "debug.hh" -#include "symbol.hh" +#include "atom.hh" #include "molecule.hh" #include "leastsquares.hh" -#include "p-col.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); -struct Stem_info { - Real x; - Real idealy; - Real miny; - int no_beams; - - - Stem_info(){} - Stem_info(Stem const *); +const int MINIMUM_STEMLEN[6] = { + 0, // just in case + 5, + 4, + 3, + 2, + 2, }; -Stem_info::Stem_info(Stem const *s) +Beam::Beam () { - x = s->hindex(); - int dir = s->dir; - idealy = max(dir*s->top, dir*s->bot); - miny = max(dir*s->minnote, dir*s-> maxnote); - assert(miny <= idealy); + slope_f = 0; + left_y = 0.0; +} +void +Beam::add (Stem*s) +{ + stems.push (s); + s->add_dependency (this); + s->beam_l_ = this; + + if (!spanned_drul_[LEFT]) + set_bounds (LEFT,s); + else + set_bounds (RIGHT,s); } -/* *************** */ +Molecule* +Beam::brew_molecule_p () const +{ + Molecule *mol_p = new Molecule; + Real inter_f = paper ()->internote_f (); + Real x0 = stems[0]->hpos_f (); + for (int j=0; j 0)? stems[j-1] : 0; + Stem * next = (j < stems.size ()-1) ? stems[j+1] :0; + + Molecule sb = stem_beams (i, next, prev); + Real x = i->hpos_f ()-x0; + sb.translate (Offset (x, (x * slope_f + left_y)* inter_f)); + mol_p->add (sb); + } + mol_p->translate_axis (x0 - spanned_drul_[LEFT]->absolute_coordinate (X_AXIS), X_AXIS); + return mol_p; +} Offset -Beam::center()const +Beam::center () const { - assert(status >= POSTCALCED); + Real w= (paper ()->note_width () + width ().length ())/2.0; + return Offset (w, (left_y + w* slope_f)*paper ()->internote_f ()); +} - Real w=(paper()->note_width() + width().length())/2.0; - return Offset(w, (left_pos + w* slope)*paper()->internote()); +void +Beam::do_pre_processing () +{ + if (!dir_) + set_default_dir (); } +void +Beam::do_print () const +{ +#ifndef NPRINT + DOUT << "slope_f " <is_type_b (Stem::static_name ())) + stems.substitute ((Stem*)o->item (), n? (Stem*) n->item ():0); +} + +Interval +Beam::do_width () const { - stems.bottom().add(s); - s->add_dependency(this); - s->print_flag = false; + return Interval (stems[0]->hpos_f (), + stems.top ()->hpos_f ()); } void -Beam::set_default_dir() +Beam::set_default_dir () { - int dirs[2]; - dirs[0]=0; dirs[1] =0; - for (iter_top(stems,i); i.ok(); i++) { - int d = i->get_default_dir(); - dirs[(d+1)/2] ++; - } - dir_i_ = (dirs[0] > dirs[1]) ? -1 : 1; - for (iter_top(stems,i); i.ok(); i++) { - i->dir = dir_i_; + Drul_array total; + total[UP] = total[DOWN] = 0; + Drul_array count; + count[UP] = count[DOWN] = 0; + Direction d = DOWN; + + for (int i=0; i dir_ + ? (1 + d * s->dir_)/2 + : s->get_center_distance (Direction (-d)); + + if (current) + { + total[d] += current; + count[d] ++; + } + + } while ((d *= -1) != DOWN); + + do { + if (!total[d]) + count[d] = 1; + } while ((d *= -1) != DOWN); + + /* the following relation is equal to + up / up_count > down / down_count + */ + dir_ = (total[UP] * count[DOWN] > total[DOWN] * count[UP]) ? UP : DOWN; + + for (int i=0; i dir_ = dir_; } } /* should use minimum energy formulation (cf linespacing) - */ + +*/ void -Beam::solve_slope() +Beam::solve_slope () { - Array sinfo; - for (iter_top(stems,i); i.ok(); i++) { - i->set_default_extents(); - Stem_info info(i); - sinfo.push(info); + Array sinfo; + for (int j=0; j set_default_extents (); + if (i->invisible_b ()) + continue; + + Stem_info info (i); + sinfo.push (info); } - Real leftx = sinfo[0].x; - Least_squares l; - for (int i=0; i < sinfo.size(); i++) { - sinfo[i].x -= leftx; - l.input.push(Offset(sinfo[i].x, sinfo[i].idealy)); + if (! sinfo.size ()) + slope_f = left_y = 0; + else if (sinfo.size () == 1) + { + slope_f = 0; + left_y = sinfo[0].idealy_f_; } + else + { - l.minimise(slope, left_pos); - Real dy = 0.0; - for (int i=0; i < sinfo.size(); i++) { - Real y = sinfo[i].x * slope + left_pos; - Real my = sinfo[i].miny; + Real leftx = sinfo[0].x; + Least_squares l; + for (int i=0; i < sinfo.size (); i++) + { + sinfo[i].x -= leftx; + l.input.push (Offset (sinfo[i].x, sinfo[i].idealy_f_)); + } - if (my - y > dy) - dy = my -y; + l.minimise (slope_f, left_y); } - left_pos += dy; - left_pos *= dir_i_; - slope *= dir_i_; - - // URG - Real sl = slope*paper()->internote(); - paper()->lookup_l()->beam(sl, 20 PT); - slope = sl /paper()->internote(); -} -void -Beam::set_stemlens() -{ - iter_top(stems,s); - Real x0 = s->hindex(); - for (; s.ok() ; s++) { - Real x = s->hindex()-x0; - s->set_stemend(left_pos + slope * x); + 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_; + + if (my - y > dy) + dy = my -y; } -} + 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] + */ + slope_f = 0.6 * tanh (slope_f); + quantise_yspan (); + + // y-values traditionally use internote dimension: therefore slope = (y/in)/x + // but mf and beam-lookup use PT dimension for y (as used for x-values) + // ugh --- there goes our simplified but careful quantisation + Real sl = slope_f * paper ()->internote_f (); + paper ()->lookup_l ()->beam (sl, 20 PT); + slope_f = sl / paper ()->internote_f (); +} void -Beam::do_post_processing() +Beam::quantise_yspan () { - solve_slope(); - set_stemlens(); + /* + [Ross] (simplification of) + Try to set slope_f complying with y-span of: + - zero + - beam_thickness / 2 + staffline_thickness / 2 + - beam_thickness + staffline_thickness + + n * interline + */ + Real interline_f = paper ()->interline_f (); + Real internote_f = interline_f / 2; + Real staffline_thickness = paper ()->rule_thickness (); + Real beam_thickness = 0.48 * (interline_f - staffline_thickness); + + const int QUANTS = 3; + Real qdy[QUANTS] = { + 0, + beam_thickness / 2 + staffline_thickness / 2, + beam_thickness + staffline_thickness + }; + + Real xspan_f = stems.top ()->hpos_f () - stems[0]->hpos_f (); + // y-values traditionally use internote dimension: therefore slope = (y/in)/x + Real yspan_f = xspan_f * abs (slope_f * internote_f); + int yspan_i = (int)(yspan_f / interline_f); + Real q = (yspan_f / interline_f - yspan_i) * interline_f; + int i = 0; + for (; i < QUANTS - 1; i++) + if ((q >= qdy[i]) && (q <= qdy[i + 1])) + { + if (q - qdy[i] < qdy[i + 1] - q) + break; + else + { + i++; + break; + } + } + q = qdy[i]; + + yspan_f = (Real)yspan_i * interline_f + q; + // y-values traditionally use internote dimension: therefore slope = (y/in)/x + slope_f = yspan_f / xspan_f / internote_f * sign (slope_f); } void -Beam::set_grouping(Rhythmic_grouping def, Rhythmic_grouping cur) +Beam::quantise_left_y (Beam::Pos pos, bool extend_b) { - def.OK(); - cur.OK(); - assert(cur.children.size() == stems.size()); - - cur.split(def); - - Array b; + /* + quantising left y should suffice, as slope is quantised too + if extend then stems must not get shorter + */ + + Real interline_f = paper ()->interline_f (); + Real internote_f = interline_f / 2; + Real staffline_thickness = paper ()->rule_thickness (); + Real beam_thickness = 0.48 * (interline_f - staffline_thickness); + + const int QUANTS = 6; + Real qy[QUANTS] = { + -staffline_thickness, + beam_thickness / 2, + beam_thickness + staffline_thickness / 2, + interline_f / 2 + beam_thickness / 2 + staffline_thickness / 2, + interline_f - staffline_thickness, + interline_f + beam_thickness / 2, + }; + /* + ugh, using i triggers gcc 2.7.2.1 internal compiler error (far down): + for (int i = 0; i < QUANTS; i++) + */ + for (int ii = 0; ii < QUANTS; ii++) + qy[ii] -= beam_thickness / 2; + Pos qpos[QUANTS] = { + HANG, + STRADDLE, + SIT, + INTER, + HANG, + STRADDLE + }; + + // y-values traditionally use internote dimension + Real y = left_y * internote_f; + int y_i = (int)floor(y / interline_f); + y = (y / interline_f - y_i) * interline_f; + + if (y < 0) + for (int ii = 0; ii < QUANTS; ii++) + qy[ii] -= interline_f; + + int lower_i = 0; + int i = 0; + for (; i < QUANTS; i++) { - iter_top(stems,s); - Array flags; - for (; s.ok(); s++) { - int f = intlog2(abs(s->flag))-2; - assert(f>0); - flags.push(f); - } - int fi =0; - b= cur.generate_beams(flags, fi); - b.insert(0,0); - b.push(0); - assert(stems.size() == b.size()/2); + if (qy[i] > y) + break; + // found if lower_i is allowed, and nearer (from below) y than new pos + if ((pos & qpos[lower_i]) && (y - qy[lower_i] < y - qy[i])) + break; + // if new pos is allowed or old pos isn't: assign new pos + if ((pos & qpos[i]) || !(pos & qpos[lower_i])) + lower_i = i; } - iter_top(stems,s); - for (int i=0; i < b.size() && s.ok(); i+=2, s++) { - s->beams_left = b[i]; - s->beams_right = b[i+1]; + int upper_i = QUANTS - 1; + for (i = QUANTS - 1; i >= 0; i--) + { + if (qy[i] < y) + break; + // found if upper_i is allowed, and nearer (from above) y than new pos + if ((pos & qpos[upper_i]) && (qy[upper_i] - y < qy[i] - y)) + break; + // if new pos is allowed or old pos isn't: assign new pos + if ((pos & qpos[i]) || !(pos & qpos[upper_i])) + upper_i = i; } -} + // y-values traditionally use internote dimension + Real upper_y = (qy[upper_i] + interline_f * y_i) / internote_f; + Real lower_y = (qy[lower_i] + interline_f * y_i) / internote_f; -// todo. -Spanner * -Beam::do_break_at( PCol *, PCol *) const -{ - Beam *beam_p= new Beam(*this); - - return beam_p; + if (extend_b) + left_y = (dir_ > 0 ? upper_y : lower_y); + else + left_y = (upper_y - left_y < y - lower_y ? upper_y : lower_y); } void -Beam::do_pre_processing() +Beam::set_stemlens () { - left_col_l_ = (*stems.top()) ->pcol_l_; - right_col_l_ = (*stems.bottom())->pcol_l_; - assert(stems.size()>1); - if (!dir_i_) - set_default_dir(); - + Real x0 = stems[0]->hpos_f (); + Real dy = 0; + + Real interline_f = paper ()->interline_f (); + Real internote_f = interline_f / 2; + Real staffline_thickness = paper ()->rule_thickness (); + Real beam_thickness = 0.48 * (interline_f - staffline_thickness); + Real interbeam_f = paper ()->interbeam_f (); + Real xspan_f = stems.top ()->hpos_f () - stems[0]->hpos_f (); + /* + ugh, y values are in "internote" dimension + */ + Real yspan_f = xspan_f * abs (slope_f * internote_f); + int yspan_i = (int)(yspan_f / interline_f); + + Pos left_pos = NONE; + + if (yspan_f < staffline_thickness / 2) + left_pos = (Pos)(STRADDLE | SIT | HANG); + else + left_pos = (Pos) (sign (slope_f) > 0 ? STRADDLE | HANG + : SIT | STRADDLE); + + /* + ugh, slope currently mangled by availability mf chars... + be more generous regarding beam position between stafflines + */ + Real q = (yspan_f / interline_f - yspan_i) * interline_f; + if (q < interline_f / 3 - beam_thickness / 2) + left_pos = (Pos) (left_pos | INTER); + + if (stems[0]->beams_right_i_ > 1) + left_pos = (Pos) (dir_ > 0 ? HANG : SIT); + + // ugh, rounding problems! + const Real EPSILON = interline_f / 10; + do + { + left_y += dy * dir_; + quantise_left_y (left_pos, dy); + dy = 0; + for (int j=0; j < stems.size (); j++) + { + Stem *s = stems[j]; + + Real x = s->hpos_f () - x0; + s->set_stemend (left_y + slope_f * x); + Real y = s->stem_length_f (); + int mult = max (stems[j]->beams_left_i_, stems[j]->beams_right_i_); + if (mult > 1) + // dim(y) = internote + y -= (mult - 1) * interbeam_f / internote_f; + if (y < MINIMUM_STEMLEN[mult]) + dy = dy >? (MINIMUM_STEMLEN[mult] - y); + } + } while (abs (dy) > EPSILON); } - -Interval -Beam::width() const +void +Beam::set_grouping (Rhythmic_grouping def, Rhythmic_grouping cur) { - Beam * me = (Beam*) this; // ugh - return Interval( (*me->stems.top()) ->hindex(), - (*me->stems.bottom()) ->hindex() ); + def.OK (); + cur.OK (); + assert (cur.children.size () == stems.size ()); + + cur.split (def); + + Array b; + { + Array flags; + for (int j=0; j flag_i_ - 2; + assert (f>0); + flags.push (f); + } + int fi =0; + b= cur.generate_beams (flags, fi); + b.insert (0,0); + b.push (0); + assert (stems.size () == b.size ()/2); + } + + for (int j=0, i=0; i < b.size () && j beams_left_i_ = b[i]; + s->beams_right_i_ = b[i+1]; + } } /* beams to go with one stem. */ Molecule -Beam::stem_beams(Stem *here, Stem *next, Stem *prev)const +Beam::stem_beams (Stem *here, Stem *next, Stem *prev) const { - assert( !next || next->hindex() > here->hindex() ); - assert( !prev || prev->hindex() < here->hindex() ); - Real dy=paper()->internote()*2; - Real stemdx = paper()->rule_thickness(); - Real sl = slope*paper()->internote(); - paper()->lookup_l()->beam(sl, 20 PT); - - Molecule leftbeams; - Molecule rightbeams; - - /* half beams extending to the left. */ - if (prev) { - int lhalfs= lhalfs = here->beams_left - prev->beams_right ; - int lwholebeams= here->beams_left beams_right ; - Real w = (here->hindex() - prev->hindex())/4; - Symbol dummy; - Atom a(dummy); - if (lhalfs) // generates warnings if not - a = paper()->lookup_l()->beam(sl, w); - a.translate(Offset (-w, -w * sl)); - for (int j = 0; j < lhalfs; j++) { - Atom b(a); - b.translate(Offset(0, -dir_i_ * dy * (lwholebeams+j))); - leftbeams.add( b ); + assert (!next || next->hpos_f () > here->hpos_f ()); + assert (!prev || prev->hpos_f () < here->hpos_f ()); + // Real dy=paper ()->internote_f ()*2; + Real dy = paper ()->interbeam_f (); + Real stemdx = paper ()->rule_thickness (); + Real sl = slope_f*paper ()->internote_f (); + paper ()->lookup_l ()->beam (sl, 20 PT); + + Molecule leftbeams; + Molecule rightbeams; + + /* 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_ beams_right_i_ ; + Real w = (here->hpos_f () - prev->hpos_f ())/4; + Atom a; + if (lhalfs) // generates warnings if not + a = paper ()->lookup_l ()->beam (sl, w); + 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); } } - - if (next){ - int rhalfs = here->beams_right - next->beams_left; - int rwholebeams = here->beams_right beams_left; - - Real w = next->hindex() - here->hindex(); - Atom a = paper()->lookup_l()->beam(sl, w + stemdx); - - int j = 0; - for (; j < rwholebeams; j++) { - Atom b(a); - b.translate(Offset(0, -dir_i_ * dy * j)); - rightbeams.add( b ); - } - w /= 4; - if (rhalfs) - a = paper()->lookup_l()->beam(sl, w); - - for (; j < rwholebeams + rhalfs; j++) { - Atom b(a); - b.translate(Offset(0, -dir_i_ * dy * j)); - rightbeams.add(b ); + if (next) + { + int rhalfs = here->beams_right_i_ - next->beams_left_i_; + int rwholebeams = here->beams_right_i_ beams_left_i_; + + Real w = next->hpos_f () - here->hpos_f (); + Atom a = paper ()->lookup_l ()->beam (sl, w + stemdx); + + int j = 0; + Real gap_f = 0; + if (here->beam_gap_i_) + { + int nogap = rwholebeams - here->beam_gap_i_; + for (; j < nogap; j++) + { + Atom b (a); + b.translate_axis (-dir_ * dy * j, Y_AXIS); + rightbeams.add (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); } - - } - leftbeams.add(rightbeams); - return leftbeams; -} - - -Molecule* -Beam::brew_molecule_p() const return out; -{ - Real inter=paper()->internote(); - out = new Molecule; - Real x0 = stems.top()->hindex(); - - for (iter_top(stems,i); i.ok(); i++) { - PCursor p(i-1); - PCursor n(i+1); - Stem * prev = p.ok() ? p.ptr() : 0; - Stem * next = n.ok() ? n.ptr() : 0; - - Molecule sb = stem_beams(i, next, prev); - Real x = i->hindex()-x0; - sb.translate(Offset(x, (x * slope + left_pos)* inter)); - out->add(sb); - } - out->translate(Offset(x0 - left_col_l_->hpos,0)); -} -IMPLEMENT_STATIC_NAME(Beam); + for (; j < rwholebeams; j++) + { + Atom b (a); + b.translate (Offset (gap_f, -dir_ * dy * j)); + rightbeams.add (b); + } -void -Beam::do_print()const -{ -#ifndef NPRINT - mtor << "slope " <lookup_l ()->beam (sl, w); -Beam::~Beam() -{ + for (; j < rwholebeams + rhalfs; j++) + { + Atom b (a); + b.translate_axis (-dir_ * dy * j, Y_AXIS); + rightbeams.add (b); + } + } + leftbeams.add (rightbeams); + return leftbeams; }