2 beam.cc -- implement Beam
4 source file of the GNU LilyPond music typesetter
6 (c) 1997--1998, 1998 Han-Wen Nienhuys <hanwen@stack.nl>
7 Jan Nieuwenhuizen <jan@digicash.com>
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"
37 #include "main.hh" // experimental features
40 IMPLEMENT_IS_TYPE_B1 (Beam, Spanner);
48 quantisation_ = NORMAL;
56 s->add_dependency (this);
59 if (!spanned_drul_[LEFT])
66 Beam::brew_molecule_p () const
68 Molecule *mol_p = new Molecule;
70 Real internote_f = paper ()->internote_f ();
72 Real x0 = stems_[0]->hpos_f ();
73 for (int j=0; j <stems_.size (); j++)
76 Stem * prev = (j > 0)? stems_[j-1] : 0;
77 Stem * next = (j < stems_.size ()-1) ? stems_[j+1] :0;
79 Molecule sb = stem_beams (i, next, prev);
80 Real x = i->hpos_f ()-x0;
81 sb.translate (Offset (x, (x * slope_f_ + left_y_) * internote_f));
84 mol_p->translate_axis (x0
85 - spanned_drul_[LEFT]->absolute_coordinate (X_AXIS), X_AXIS);
93 Real w= (paper ()->note_width () + width ().length ())/2.0;
94 return Offset (w, (left_y_ + w* slope_f_)*paper ()->internote_f ());
98 Beam::do_pre_processing ()
105 Beam::do_print () const
108 DOUT << "slope_f_ " <<slope_f_ << "left ypos " << left_y_;
109 Spanner::do_print ();
114 Beam::do_post_processing ()
116 if (stems_.size () < 2)
118 warning (_ ("Beam with less than 2 stems"));
119 transparent_b_ = true;
127 Beam::do_substitute_dependent (Score_elem*o,Score_elem*n)
129 if (o->is_type_b (Stem::static_name ()))
130 stems_.substitute ((Stem*)o->item (), n? (Stem*) n->item ():0);
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.
179 // fixme. make runtime.
181 // dir_ = (count[UP] > count[DOWN]) ? UP : DOWN;
183 // mean centre distance
184 dir_ = (total[UP] > total[DOWN]) ? UP : DOWN;
186 for (int i=0; i <stems_.size (); i++)
190 if (!s->dir_forced_b_)
196 See Documentation/tex/fonts.doc
202 should use minimum energy formulation (cf linespacing)
205 assert (multiple_i_);
206 Array<Stem_info> sinfo;
207 DOUT << "Beam::solve_slope: \n";
208 for (int j=0; j <stems_.size (); j++)
212 i->mult_i_ = multiple_i_;
213 i->set_default_extents ();
214 if (i->invisible_b ())
221 slope_f_ = left_y_ = 0;
222 else if (sinfo.size () == 1)
225 left_y_ = sinfo[0].idealy_f_;
229 Real leftx = sinfo[0].x_;
231 for (int i=0; i < sinfo.size (); i++)
233 sinfo[i].x_ -= leftx;
234 l.input.push (Offset (sinfo[i].x_, sinfo[i].idealy_f_));
237 l.minimise (slope_f_, left_y_);
241 solved_slope_f_ = dir_ * slope_f_;
244 This neat trick is by Werner Lemberg, damped = tanh (slope_f_) corresponds
245 with some tables in [Wanske]
248 slope_f_ = 0.6 * tanh (slope_f_) / damping_i_;
254 dropping lq for stemlengths solves [d d d] [d g d] "bug..."
256 but may be a bit too crude, and result in lots of
259 perhaps only if slope = 0 ?
262 // left_y_ = sinfo[0].minyf_;
264 if (sinfo.size () >= 1)
266 Real staffline_f = paper ()->rule_thickness ();
267 Real epsilon_f = staffline_f / 8;
268 if (abs (slope_f_) < epsilon_f)
269 left_y_ = (sinfo[0].idealy_f_ + sinfo.top ().idealy_f_) / 2;
272 symmetrical, but results often in having stemlength = minimal
274 left_y_ = sinfo[0].dir_ == dir_ ? sinfo[0].miny_f_ : sinfo[0].maxy_f_;
279 Real dx = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
280 if (sinfo[0].dir_ == sinfo.top ().dir_)
281 left_y_ = sinfo[0].idealy_f_ >? sinfo.top ().idealy_f_ - slope_f_ * dx;
284 left_y_ = sinfo[0].idealy_f_;
290 for (int i=0; i < sinfo.size (); i++)
292 Real y = sinfo[i].x_ * slope_f_ + left_y_;
293 Real my = sinfo[i].miny_f_;
309 [Ross] (simplification of)
310 Try to set slope_f_ complying with y-span of:
312 - beam_f / 2 + staffline_f / 2
313 - beam_f + staffline_f
317 if (quantisation_ <= NONE)
320 Real interline_f = paper ()->interline_f ();
321 Real internote_f = interline_f / 2;
322 Real staffline_f = paper ()->rule_thickness ();
323 Real beam_f = paper ()->beam_thickness_f ();
325 Real dx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
327 // dim(y) = internote; so slope = (y/internote)/x
328 Real dy_f = dx_f * abs (slope_f_ * internote_f);
332 /* UGR. ICE in 2.8.1; bugreport filed. */
333 Array<Real> allowed_fraction (3);
334 allowed_fraction[0] = 0;
335 allowed_fraction[1] = (beam_f / 2 + staffline_f / 2);
336 allowed_fraction[2] = (beam_f + staffline_f);
339 Interval iv = quantise_iv (allowed_fraction, interline_f, dy_f);
340 quanty_f = (dy_f - iv.min () <= iv.max () - dy_f)
345 slope_f_ = (quanty_f / dx_f) / internote_f * sign (slope_f_);
348 static int test_pos = 0;
353 Prevent interference from stafflines and beams. See Documentation/tex/fonts.doc
357 Beam::quantise_left_y (bool extend_b)
360 we only need to quantise the start of the beam as dy is quantised too
361 if extend_b then stems must *not* get shorter
364 if (quantisation_ <= NONE)
368 ----------------------------------------------------------
372 --------------########------------------------------------
375 hang straddle sit inter hang
378 Real interline_f = paper ()->interline_f ();
379 Real internote_f = paper ()->internote_f ();
380 Real staffline_f = paper ()->rule_thickness ();
381 Real beam_f = paper ()->beam_thickness_f ();
385 it would be nice to have all allowed positions in a runtime matrix:
386 (multiplicity, minimum_beam_dy, maximum_beam_dy)
390 Real sit = beam_f / 2 - staffline_f / 2;
391 Real inter = interline_f / 2;
392 Real hang = interline_f - beam_f / 2 + staffline_f / 2;
395 Put all allowed positions into an array.
396 Whether a position is allowed or not depends on
397 strictness of quantisation, multiplicity and direction.
399 For simplicity, we'll assume dir = UP and correct if
400 dir = DOWN afterwards.
403 // dim(left_y_) = internote
404 Real dy_f = dir_ * left_y_ * internote_f;
406 Real beamdx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
407 Real beamdy_f = beamdx_f * slope_f_ * internote_f;
409 Array<Real> allowed_position;
410 if (quantisation_ != TEST)
412 if (quantisation_ <= NORMAL)
414 if ((multiple_i_ <= 2) || (abs (beamdy_f) >= staffline_f / 2))
415 allowed_position.push (straddle);
416 if ((multiple_i_ <= 1) || (abs (beamdy_f) >= staffline_f / 2))
417 allowed_position.push (sit);
418 allowed_position.push (hang);
421 // TODO: check and fix TRADITIONAL
423 if ((multiple_i_ <= 2) || (abs (beamdy_f) >= staffline_f / 2))
424 allowed_position.push (straddle);
425 if ((multiple_i_ <= 1) && (beamdy_f <= staffline_f / 2))
426 allowed_position.push (sit);
427 if (beamdy_f >= -staffline_f / 2)
428 allowed_position.push (hang);
435 allowed_position.push (hang);
436 cout << "hang" << hang << endl;
438 else if (test_pos==1)
440 allowed_position.push (straddle);
441 cout << "straddle" << straddle << endl;
443 else if (test_pos==2)
445 allowed_position.push (sit);
446 cout << "sit" << sit << endl;
448 else if (test_pos==3)
450 allowed_position.push (inter);
451 cout << "inter" << inter << endl;
456 // this currently never happens
457 Real q = (dy_f / interline_f - dy_i) * interline_f;
458 if ((quantisation_ < NORMAL) && (q < interline_f / 3 - beam_f / 2))
459 allowed_position.push (inter);
462 Interval iv = quantise_iv (allowed_position, interline_f, dy_f);
464 Real quanty_f = dy_f - iv.min () <= iv.max () - dy_f ? iv.min () : iv.max ();
466 quanty_f = iv.max ();
468 // dim(left_y_) = internote
469 left_y_ = dir_ * quanty_f / internote_f;
473 Beam::set_stemlens ()
475 Real staffline_f = paper ()->rule_thickness ();
476 Real interbeam_f = paper ()->interbeam_f (multiple_i_);
477 Real internote_f = paper ()->internote_f ();
478 Real beam_f = paper ()->beam_thickness_f ();
481 Real epsilon_f = staffline_f / 8;
485 Damped and quantised slopes, esp. in monotone scales such as
489 will soon produce the minimal stem-length for one of the extreme
490 stems, which is wrong (and ugly). The minimum stemlength should
491 be kept rather small, in order to handle extreme beaming, such as
493 [c c' 'c] %assuming no knee
496 To avoid these short stems for normal cases, we'll correct for
497 the loss in slope, if necessary.
500 ugh, another hack. who's next?
501 Writing this all down, i realise (at last) that the Right Thing to
502 do is to assign uglyness to slope and stem-lengths and then minimise
503 the total uglyness of a beam.
504 Steep slopes are ugly, shortened stems are ugly, lengthened stems
510 Real dx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
511 Real damp_correct_f = paper ()->get_var ("beam_slope_damp_correct_factor");
512 Real damped_slope_dy_f = (solved_slope_f_ - slope_f_) * dx_f
514 damped_slope_dy_f *= damp_correct_f;
515 if (damped_slope_dy_f <= epsilon_f)
516 damped_slope_dy_f = 0;
518 DOUT << "Beam::set_stemlens: \n";
519 Real x0 = stems_[0]->hpos_f ();
522 for (int jj = 0; jj < 10; jj++)
524 left_y_ += dy_f * dir_;
525 quantise_left_y (dy_f);
527 for (int i=0; i < stems_.size (); i++)
530 if (s->transparent_b_)
533 Real x = s->hpos_f () - x0;
534 // urg move this to stem-info
535 Real sy = left_y_ + slope_f_ * x;
537 sy -= dir_ * (beam_f / 2
538 + (s->mult_i_ - 1) * interbeam_f) / internote_f;
540 Real y = s->stem_end_f () * dir_;
542 if (y > info.maxy_f_)
543 dy_f = dy_f <? info.maxy_f_ - y;
544 if (y < info.miny_f_)
546 // when all too short, normal stems win..
547 if (dy_f < -epsilon_f)
548 warning ( _("Weird beam shift, check your knees."));
549 dy_f = dy_f >? info.miny_f_ - y;
552 if (damped_slope_dy_f && (dy_f >= 0))
553 dy_f += damped_slope_dy_f;
554 damped_slope_dy_f = 0;
555 if (abs (dy_f) <= epsilon_f)
557 DOUT << "Beam::set_stemlens: " << jj << " iterations\n";
568 ugh. this is broken and should be rewritten.
572 Beam::set_grouping (Rhythmic_grouping def, Rhythmic_grouping cur)
576 assert (cur.children.size () == stems_.size ());
583 for (int j=0; j <stems_.size (); j++)
587 int f = s->flag_i_ - 2;
592 b= cur.generate_beams (flags, fi);
595 assert (stems_.size () == b.size ()/2);
598 for (int j=0, i=0; i < b.size () && j <stems_.size (); i+= 2, j++)
601 s->beams_left_i_ = b[i];
602 s->beams_right_i_ = b[i+1];
603 multiple_i_ = multiple_i_ >? (b[i] >? b[i+1]);
608 beams to go with one stem.
611 Beam::stem_beams (Stem *here, Stem *next, Stem *prev) const
613 assert (!next || next->hpos_f () > here->hpos_f ());
614 assert (!prev || prev->hpos_f () < here->hpos_f ());
616 Real staffline_f = paper ()->rule_thickness ();
617 Real interbeam_f = paper ()->interbeam_f (multiple_i_);
618 Real internote_f = paper ()->internote_f ();
619 Real beam_f = paper ()->beam_thickness_f ();
621 Real dy = interbeam_f;
622 Real stemdx = staffline_f;
623 Real sl = slope_f_* internote_f;
624 paper ()->lookup_l ()->beam (sl, 20 PT, 1 PT);
629 /* half beams extending to the left. */
632 int lhalfs= lhalfs = here->beams_left_i_ - prev->beams_right_i_ ;
633 int lwholebeams= here->beams_left_i_ <? prev->beams_right_i_ ;
634 Real w = (here->hpos_f () - prev->hpos_f ())/4 <? paper ()->note_width ();;
636 if (lhalfs) // generates warnings if not
637 a = paper ()->lookup_l ()->beam (sl, w, beam_f);
638 a.translate (Offset (-w, -w * sl));
639 for (int j = 0; j < lhalfs; j++)
642 b.translate_axis (-dir_ * dy * (lwholebeams+j), Y_AXIS);
649 int rhalfs = here->beams_right_i_ - next->beams_left_i_;
650 int rwholebeams = here->beams_right_i_ <? next->beams_left_i_;
652 Real w = next->hpos_f () - here->hpos_f ();
653 Atom a = paper ()->lookup_l ()->beam (sl, w + stemdx, beam_f);
654 a.translate_axis( - stemdx/2, X_AXIS);
657 if (here->beam_gap_i_)
659 int nogap = rwholebeams - here->beam_gap_i_;
660 for (; j < nogap; j++)
663 b.translate_axis (-dir_ * dy * j, Y_AXIS);
666 // TODO: notehead widths differ for different types
667 gap_f = paper ()->note_width () / 2;
669 a = paper ()->lookup_l ()->beam (sl, w + stemdx, beam_f);
672 for (; j < rwholebeams; j++)
675 b.translate (Offset (gap_f, -dir_ * dy * j));
679 w = w/4 <? paper ()->note_width ();
681 a = paper ()->lookup_l ()->beam (sl, w, beam_f);
683 for (; j < rwholebeams + rhalfs; j++)
686 b.translate_axis (-dir_ * dy * j, Y_AXIS);
691 leftbeams.add (rightbeams);
694 Does beam quanting think of the asymetry of beams?
695 Refpoint is on bottom of symbol. (FIXTHAT) --hwn.