2 beam.cc -- implement Beam
4 source file of the GNU LilyPond music typesetter
6 (c) 1997--1998, 1998 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7 Jan Nieuwenhuizen <janneke@gnu.org>
24 #include "dimensions.hh"
26 #include "abbreviation-beam.hh"
30 #include "molecule.hh"
31 #include "leastsquares.hh"
33 #include "paper-def.hh"
35 #include "grouping.hh"
36 #include "stem-info.hh"
39 IMPLEMENT_IS_TYPE_B1 (Beam, Spanner);
47 quantisation_ = NORMAL;
52 Beam::add_stem (Stem*s)
55 s->add_dependency (this);
58 if (!spanned_drul_[LEFT])
65 Beam::brew_molecule_p () const
67 Molecule *mol_p = new Molecule;
69 Real internote_f = paper ()->internote_f ();
71 Real x0 = stems_[0]->hpos_f ();
72 for (int j=0; j <stems_.size (); j++)
75 Stem * prev = (j > 0)? stems_[j-1] : 0;
76 Stem * next = (j < stems_.size ()-1) ? stems_[j+1] :0;
78 Molecule sb = stem_beams (i, next, prev);
79 Real x = i->hpos_f ()-x0;
80 sb.translate (Offset (x, (x * slope_f_ + left_y_) * internote_f));
81 mol_p->add_molecule (sb);
83 mol_p->translate_axis (x0
84 - spanned_drul_[LEFT]->absolute_coordinate (X_AXIS), X_AXIS);
92 Real w= (paper ()->note_width () + width ().length ())/2.0;
93 return Offset (w, (left_y_ + w* slope_f_)*paper ()->internote_f ());
97 Beam::do_pre_processing ()
104 Beam::do_print () const
107 DOUT << "slope_f_ " << slope_f_ << "left ypos " << left_y_;
108 Spanner::do_print ();
113 Beam::do_post_processing ()
115 if (stems_.size () < 2)
117 warning (_ ("beam with less than two stems"));
118 transparent_b_ = true;
126 Beam::do_substitute_dependent (Score_element*o,Score_element*n)
128 if (Stem * os = dynamic_cast<Stem*> (o))
129 stems_.substitute (os,
130 dynamic_cast<Stem *> (n));
134 Beam::do_width () const
136 return Interval (stems_[0]->hpos_f (),
137 stems_.top ()->hpos_f ());
141 Beam::set_default_dir ()
143 Drul_array<int> total;
144 total[UP] = total[DOWN] = 0;
145 Drul_array<int> count;
146 count[UP] = count[DOWN] = 0;
149 for (int i=0; i <stems_.size (); i++)
152 int current = s->dir_
153 ? (1 + d * s->dir_)/2
154 : s->get_center_distance ((Direction)-d);
162 } while (flip(&d) != DOWN);
167 } while (flip(&d) != DOWN);
170 [Ross] states that the majority of the notes dictates the
171 direction (and not the mean of "center distance")
173 But is that because it really looks better, or because he
174 wants to provide some real simple hands-on rules.
176 We have our doubts, so we simply provide all sensible alternatives.
179 Dir_algorithm a = (Dir_algorithm)rint(paper ()->get_var ("beam_dir_algorithm"));
183 dir_ = (count[UP] > count[DOWN]) ? UP : DOWN;
186 // mean centre distance
187 dir_ = (total[UP] > total[DOWN]) ? UP : DOWN;
191 // median centre distance
194 else if (!count[DOWN])
197 dir_ = (total[UP] / count[UP] > total[DOWN] / count[DOWN]) ? UP : DOWN;
201 for (int i=0; i <stems_.size (); i++)
205 if (!s->dir_forced_b_)
211 See Documentation/tex/fonts.doc
217 should use minimum energy formulation (cf linespacing)
220 assert (multiple_i_);
221 Array<Stem_info> sinfo;
222 DOUT << "Beam::solve_slope: \n";
223 for (int j=0; j <stems_.size (); j++)
227 i->mult_i_ = multiple_i_;
228 i->set_default_extents ();
229 if (i->invisible_b ())
236 slope_f_ = left_y_ = 0;
237 else if (sinfo.size () == 1)
240 left_y_ = sinfo[0].idealy_f_;
244 Real leftx = sinfo[0].x_;
246 for (int i=0; i < sinfo.size (); i++)
248 sinfo[i].x_ -= leftx;
249 l.input.push (Offset (sinfo[i].x_, sinfo[i].idealy_f_));
252 l.minimise (slope_f_, left_y_);
256 solved_slope_f_ = dir_ * slope_f_;
259 This neat trick is by Werner Lemberg, damped = tanh (slope_f_) corresponds
260 with some tables in [Wanske]
263 slope_f_ = 0.6 * tanh (slope_f_) / damping_i_;
269 dropping lq for stemlengths solves [d d d] [d g d] "bug..."
271 but may be a bit too crude, and result in lots of
274 perhaps only if slope = 0 ?
277 // left_y_ = sinfo[0].minyf_;
279 if (sinfo.size () >= 1)
281 Real staffline_f = paper ()->rule_thickness ();
282 Real epsilon_f = staffline_f / 8;
283 if (abs (slope_f_) < epsilon_f)
284 left_y_ = (sinfo[0].idealy_f_ + sinfo.top ().idealy_f_) / 2;
287 symmetrical, but results often in having stemlength = minimal
289 left_y_ = sinfo[0].dir_ == dir_ ? sinfo[0].miny_f_ : sinfo[0].maxy_f_;
294 Real dx = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
295 if (sinfo[0].dir_ == sinfo.top ().dir_)
296 left_y_ = sinfo[0].idealy_f_ >? sinfo.top ().idealy_f_ - slope_f_ * dx;
299 left_y_ = sinfo[0].idealy_f_;
305 for (int i=0; i < sinfo.size (); i++)
307 Real y = sinfo[i].x_ * slope_f_ + left_y_;
308 Real my = sinfo[i].miny_f_;
324 [Ross] (simplification of)
325 Try to set slope_f_ complying with y-span of:
327 - beam_f / 2 + staffline_f / 2
328 - beam_f + staffline_f
332 if (quantisation_ <= NONE)
335 Real interline_f = paper ()->interline_f ();
336 Real internote_f = interline_f / 2;
337 Real staffline_f = paper ()->rule_thickness ();
338 Real beam_f = paper ()->beam_thickness_f ();
340 Real dx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
342 // dim(y) = internote; so slope = (y/internote)/x
343 Real dy_f = dx_f * abs (slope_f_ * internote_f);
347 /* UGR. ICE in 2.8.1; bugreport filed. */
348 Array<Real> allowed_fraction (3);
349 allowed_fraction[0] = 0;
350 allowed_fraction[1] = (beam_f / 2 + staffline_f / 2);
351 allowed_fraction[2] = (beam_f + staffline_f);
354 Interval iv = quantise_iv (allowed_fraction, interline_f, dy_f);
355 quanty_f = (dy_f - iv.min () <= iv.max () - dy_f)
360 slope_f_ = (quanty_f / dx_f) / internote_f * sign (slope_f_);
363 static int test_pos = 0;
368 Prevent interference from stafflines and beams. See Documentation/tex/fonts.doc
372 Beam::quantise_left_y (bool extend_b)
375 we only need to quantise the start of the beam as dy is quantised too
376 if extend_b then stems must *not* get shorter
379 if (quantisation_ <= NONE)
383 ----------------------------------------------------------
387 --------------########------------------------------------
390 hang straddle sit inter hang
393 Real interline_f = paper ()->interline_f ();
394 Real internote_f = paper ()->internote_f ();
395 Real staffline_f = paper ()->rule_thickness ();
396 Real beam_f = paper ()->beam_thickness_f ();
400 it would be nice to have all allowed positions in a runtime matrix:
401 (multiplicity, minimum_beam_dy, maximum_beam_dy)
405 Real sit = beam_f / 2 - staffline_f / 2;
406 Real inter = interline_f / 2;
407 Real hang = interline_f - beam_f / 2 + staffline_f / 2;
410 Put all allowed positions into an array.
411 Whether a position is allowed or not depends on
412 strictness of quantisation, multiplicity and direction.
414 For simplicity, we'll assume dir = UP and correct if
415 dir = DOWN afterwards.
418 // dim(left_y_) = internote
419 Real dy_f = dir_ * left_y_ * internote_f;
421 Real beamdx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
422 Real beamdy_f = beamdx_f * slope_f_ * internote_f;
424 Array<Real> allowed_position;
425 if (quantisation_ != TEST)
427 if (quantisation_ <= NORMAL)
429 if ((multiple_i_ <= 2) || (abs (beamdy_f) >= staffline_f / 2))
430 allowed_position.push (straddle);
431 if ((multiple_i_ <= 1) || (abs (beamdy_f) >= staffline_f / 2))
432 allowed_position.push (sit);
433 allowed_position.push (hang);
436 // TODO: check and fix TRADITIONAL
438 if ((multiple_i_ <= 2) || (abs (beamdy_f) >= staffline_f / 2))
439 allowed_position.push (straddle);
440 if ((multiple_i_ <= 1) && (beamdy_f <= staffline_f / 2))
441 allowed_position.push (sit);
442 if (beamdy_f >= -staffline_f / 2)
443 allowed_position.push (hang);
450 allowed_position.push (hang);
451 cout << "hang" << hang << endl;
453 else if (test_pos==1)
455 allowed_position.push (straddle);
456 cout << "straddle" << straddle << endl;
458 else if (test_pos==2)
460 allowed_position.push (sit);
461 cout << "sit" << sit << endl;
463 else if (test_pos==3)
465 allowed_position.push (inter);
466 cout << "inter" << inter << endl;
471 // this currently never happens
472 Real q = (dy_f / interline_f - dy_i) * interline_f;
473 if ((quantisation_ < NORMAL) && (q < interline_f / 3 - beam_f / 2))
474 allowed_position.push (inter);
477 Interval iv = quantise_iv (allowed_position, interline_f, dy_f);
479 Real quanty_f = dy_f - iv.min () <= iv.max () - dy_f ? iv.min () : iv.max ();
481 quanty_f = iv.max ();
483 // dim(left_y_) = internote
484 left_y_ = dir_ * quanty_f / internote_f;
488 Beam::set_stemlens ()
490 Real staffline_f = paper ()->rule_thickness ();
491 Real interbeam_f = paper ()->interbeam_f (multiple_i_);
492 Real internote_f = paper ()->internote_f ();
493 Real beam_f = paper ()->beam_thickness_f ();
496 Real epsilon_f = staffline_f / 8;
500 Damped and quantised slopes, esp. in monotone scales such as
504 will soon produce the minimal stem-length for one of the extreme
505 stems, which is wrong (and ugly). The minimum stemlength should
506 be kept rather small, in order to handle extreme beaming, such as
508 [c c' 'c] %assuming no knee
511 To avoid these short stems for normal cases, we'll correct for
512 the loss in slope, if necessary.
515 ugh, another hack. who's next?
516 Writing this all down, i realise (at last) that the Right Thing to
517 do is to assign uglyness to slope and stem-lengths and then minimise
518 the total uglyness of a beam.
519 Steep slopes are ugly, shortened stems are ugly, lengthened stems
525 Real dx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
526 Real damp_correct_f = paper ()->get_var ("beam_slope_damp_correct_factor");
527 Real damped_slope_dy_f = (solved_slope_f_ - slope_f_) * dx_f
529 damped_slope_dy_f *= damp_correct_f;
530 if (damped_slope_dy_f <= epsilon_f)
531 damped_slope_dy_f = 0;
533 DOUT << "Beam::set_stemlens: \n";
534 Real x0 = stems_[0]->hpos_f ();
537 for (int jj = 0; jj < 10; jj++)
539 left_y_ += dy_f * dir_;
540 quantise_left_y (dy_f);
542 for (int i=0; i < stems_.size (); i++)
545 if (s->transparent_b_)
548 Real x = s->hpos_f () - x0;
549 // urg move this to stem-info
550 Real sy = left_y_ + slope_f_ * x;
552 sy -= dir_ * (beam_f / 2
553 + (s->mult_i_ - 1) * interbeam_f) / internote_f;
555 Real y = s->stem_end_f () * dir_;
557 if (y > info.maxy_f_)
558 dy_f = dy_f <? info.maxy_f_ - y;
559 if (y < info.miny_f_)
561 // when all too short, normal stems win..
562 if (dy_f < -epsilon_f)
563 warning (_ ("weird beam shift, check your knees"));
564 dy_f = dy_f >? info.miny_f_ - y;
567 if (damped_slope_dy_f && (dy_f >= 0))
568 dy_f += damped_slope_dy_f;
569 damped_slope_dy_f = 0;
570 if (abs (dy_f) <= epsilon_f)
572 DOUT << "Beam::set_stemlens: " << jj << " iterations\n";
583 ugh. this is broken and should be rewritten.
587 Beam::set_grouping (Rhythmic_grouping def, Rhythmic_grouping cur)
591 assert (cur.children.size () == stems_.size ());
598 for (int j=0; j <stems_.size (); j++)
602 int f = s->flag_i_ - 2;
607 b= cur.generate_beams (flags, fi);
610 assert (stems_.size () == b.size ()/2);
613 for (int j=0, i=0; i < b.size () && j <stems_.size (); i+= 2, j++)
616 s->beams_left_i_ = b[i];
617 s->beams_right_i_ = b[i+1];
618 multiple_i_ = multiple_i_ >? (b[i] >? b[i+1]);
623 beams to go with one stem.
626 Beam::stem_beams (Stem *here, Stem *next, Stem *prev) const
628 assert (!next || next->hpos_f () > here->hpos_f ());
629 assert (!prev || prev->hpos_f () < here->hpos_f ());
631 Real staffline_f = paper ()->rule_thickness ();
632 Real interbeam_f = paper ()->interbeam_f (multiple_i_);
633 Real internote_f = paper ()->internote_f ();
634 Real beam_f = paper ()->beam_thickness_f ();
636 Real dy = interbeam_f;
637 Real stemdx = staffline_f;
638 Real sl = slope_f_* internote_f;
639 lookup_l ()->beam (sl, 20 PT, 1 PT);
644 /* half beams extending to the left. */
647 int lhalfs= lhalfs = here->beams_left_i_ - prev->beams_right_i_ ;
648 int lwholebeams= here->beams_left_i_ <? prev->beams_right_i_ ;
650 Half beam should be one note-width,
651 but let's make sure two half-beams never touch
653 Real w = here->hpos_f () - prev->hpos_f ();
654 w = w/2 <? paper ()->note_width ();
656 if (lhalfs) // generates warnings if not
657 a = lookup_l ()->beam (sl, w, beam_f);
658 a.translate (Offset (-w, -w * sl));
659 for (int j = 0; j < lhalfs; j++)
662 b.translate_axis (-dir_ * dy * (lwholebeams+j), Y_AXIS);
663 leftbeams.add_atom (b);
669 int rhalfs = here->beams_right_i_ - next->beams_left_i_;
670 int rwholebeams = here->beams_right_i_ <? next->beams_left_i_;
672 Real w = next->hpos_f () - here->hpos_f ();
673 Atom a = lookup_l ()->beam (sl, w + stemdx, beam_f);
674 a.translate_axis( - stemdx/2, X_AXIS);
677 if (here->beam_gap_i_)
679 int nogap = rwholebeams - here->beam_gap_i_;
680 for (; j < nogap; j++)
683 b.translate_axis (-dir_ * dy * j, Y_AXIS);
684 rightbeams.add_atom (b);
686 // TODO: notehead widths differ for different types
687 gap_f = paper ()->note_width () / 2;
689 a = lookup_l ()->beam (sl, w + stemdx, beam_f);
692 for (; j < rwholebeams; j++)
695 b.translate (Offset (gap_f, -dir_ * dy * j));
696 rightbeams.add_atom (b);
699 w = w/2 <? paper ()->note_width ();
701 a = lookup_l ()->beam (sl, w, beam_f);
703 for (; j < rwholebeams + rhalfs; j++)
706 b.translate_axis (-dir_ * dy * j, Y_AXIS);
707 rightbeams.add_atom (b);
711 leftbeams.add_molecule (rightbeams);
714 Does beam quanting think of the asymetry of beams?
715 Refpoint is on bottom of symbol. (FIXTHAT) --hwn.