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 left_y_ = sinfo[0].idealy_f_ >? sinfo.top ().idealy_f_ - slope_f_ * dx;
286 for (int i=0; i < sinfo.size (); i++)
288 Real y = sinfo[i].x_ * slope_f_ + left_y_;
289 Real my = sinfo[i].miny_f_;
305 [Ross] (simplification of)
306 Try to set slope_f_ complying with y-span of:
308 - beam_f / 2 + staffline_f / 2
309 - beam_f + staffline_f
313 if (quantisation_ <= NONE)
316 Real interline_f = paper ()->interline_f ();
317 Real internote_f = interline_f / 2;
318 Real staffline_f = paper ()->rule_thickness ();
319 Real beam_f = paper ()->beam_thickness_f ();
321 Real dx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
323 // dim(y) = internote; so slope = (y/internote)/x
324 Real dy_f = dx_f * abs (slope_f_ * internote_f);
328 /* UGR. ICE in 2.8.1; bugreport filed. */
329 Array<Real> allowed_fraction (3);
330 allowed_fraction[0] = 0;
331 allowed_fraction[1] = (beam_f / 2 + staffline_f / 2);
332 allowed_fraction[2] = (beam_f + staffline_f);
335 Interval iv = quantise_iv (allowed_fraction, interline_f, dy_f);
336 quanty_f = (dy_f - iv.min () <= iv.max () - dy_f)
341 slope_f_ = (quanty_f / dx_f) / internote_f * sign (slope_f_);
344 static int test_pos = 0;
349 Prevent interference from stafflines and beams. See Documentation/tex/fonts.doc
353 Beam::quantise_left_y (bool extend_b)
356 we only need to quantise the start of the beam as dy is quantised too
357 if extend_b then stems must *not* get shorter
360 if (quantisation_ <= NONE)
364 ----------------------------------------------------------
368 --------------########------------------------------------
371 hang straddle sit inter hang
374 Real interline_f = paper ()->interline_f ();
375 Real internote_f = paper ()->internote_f ();
376 Real staffline_f = paper ()->rule_thickness ();
377 Real beam_f = paper ()->beam_thickness_f ();
381 it would be nice to have all allowed positions in a runtime matrix:
382 (multiplicity, minimum_beam_dy, maximum_beam_dy)
386 Real sit = beam_f / 2 - staffline_f / 2;
387 Real inter = interline_f / 2;
388 Real hang = interline_f - beam_f / 2 + staffline_f / 2;
391 Put all allowed positions into an array.
392 Whether a position is allowed or not depends on
393 strictness of quantisation, multiplicity and direction.
395 For simplicity, we'll assume dir = UP and correct if
396 dir = DOWN afterwards.
399 // dim(left_y_) = internote
400 Real dy_f = dir_ * left_y_ * internote_f;
402 Real beamdx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
403 Real beamdy_f = beamdx_f * slope_f_ * internote_f;
405 Array<Real> allowed_position;
406 if (quantisation_ != TEST)
408 if (quantisation_ <= NORMAL)
410 if ((multiple_i_ <= 2) || (abs (beamdy_f) >= staffline_f / 2))
411 allowed_position.push (straddle);
412 if ((multiple_i_ <= 1) || (abs (beamdy_f) >= staffline_f / 2))
413 allowed_position.push (sit);
414 allowed_position.push (hang);
417 // TODO: check and fix TRADITIONAL
419 if ((multiple_i_ <= 2) || (abs (beamdy_f) >= staffline_f / 2))
420 allowed_position.push (straddle);
421 if ((multiple_i_ <= 1) && (beamdy_f <= staffline_f / 2))
422 allowed_position.push (sit);
423 if (beamdy_f >= -staffline_f / 2)
424 allowed_position.push (hang);
431 allowed_position.push (hang);
432 cout << "hang" << hang << endl;
434 else if (test_pos==1)
436 allowed_position.push (straddle);
437 cout << "straddle" << straddle << endl;
439 else if (test_pos==2)
441 allowed_position.push (sit);
442 cout << "sit" << sit << endl;
444 else if (test_pos==3)
446 allowed_position.push (inter);
447 cout << "inter" << inter << endl;
452 // this currently never happens
453 Real q = (dy_f / interline_f - dy_i) * interline_f;
454 if ((quantisation_ < NORMAL) && (q < interline_f / 3 - beam_f / 2))
455 allowed_position.push (inter);
458 Interval iv = quantise_iv (allowed_position, interline_f, dy_f);
460 Real quanty_f = dy_f - iv.min () <= iv.max () - dy_f ? iv.min () : iv.max ();
462 quanty_f = iv.max ();
464 // dim(left_y_) = internote
465 left_y_ = dir_ * quanty_f / internote_f;
469 Beam::set_stemlens ()
471 Real staffline_f = paper ()->rule_thickness ();
472 Real interbeam_f = paper ()->interbeam_f (multiple_i_);
473 Real internote_f = paper ()->internote_f ();
474 Real beam_f = paper ()->beam_thickness_f ();
477 Real epsilon_f = staffline_f / 8;
481 Damped and quantised slopes, esp. in monotone scales such as
485 will soon produce the minimal stem-length for one of the extreme
486 stems, which is wrong (and ugly). The minimum stemlength should
487 be kept rather small, in order to handle extreme beaming, such as
489 [c c' 'c] %assuming no knee
492 To avoid these short stems for normal cases, we'll correct for
493 the loss in slope, if necessary.
496 ugh, another hack. who's next?
497 Writing this all down, i realise (at last) that the Right Thing to
498 do is to assign uglyness to slope and stem-lengths and then minimise
499 the total uglyness of a beam.
500 Steep slopes are ugly, shortened stems are ugly, lengthened stems
506 Real dx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
507 Real damp_correct_f = paper ()->get_var ("beam_slope_damp_correct_factor");
508 Real damped_slope_dy_f = (solved_slope_f_ - slope_f_) * dx_f
510 damped_slope_dy_f *= damp_correct_f;
511 if (damped_slope_dy_f <= epsilon_f)
512 damped_slope_dy_f = 0;
514 DOUT << "Beam::set_stemlens: \n";
515 Real x0 = stems_[0]->hpos_f ();
518 for (int jj = 0; jj < 10; jj++)
520 left_y_ += dy_f * dir_;
521 quantise_left_y (dy_f);
523 for (int i=0; i < stems_.size (); i++)
526 if (s->transparent_b_)
529 Real x = s->hpos_f () - x0;
530 // urg move this to stem-info
531 Real sy = left_y_ + slope_f_ * x;
533 sy -= dir_ * (beam_f / 2
534 + (s->mult_i_ - 1) * interbeam_f) / internote_f;
536 Real y = s->stem_end_f () * dir_;
538 if (y > info.maxy_f_)
539 dy_f = dy_f <? info.maxy_f_ - y;
540 if (y < info.miny_f_)
542 // when all too short, normal stems win..
543 if (dy_f < -epsilon_f)
544 warning ( _("Weird beam shift, check your knees."));
545 dy_f = dy_f >? info.miny_f_ - y;
548 if (damped_slope_dy_f && (dy_f >= 0))
549 dy_f += damped_slope_dy_f;
550 damped_slope_dy_f = 0;
551 if (abs (dy_f) <= epsilon_f)
553 DOUT << "Beam::set_stemlens: " << jj << " iterations\n";
564 ugh. this is broken and should be rewritten.
568 Beam::set_grouping (Rhythmic_grouping def, Rhythmic_grouping cur)
572 assert (cur.children.size () == stems_.size ());
579 for (int j=0; j <stems_.size (); j++)
583 int f = s->flag_i_ - 2;
588 b= cur.generate_beams (flags, fi);
591 assert (stems_.size () == b.size ()/2);
594 for (int j=0, i=0; i < b.size () && j <stems_.size (); i+= 2, j++)
597 s->beams_left_i_ = b[i];
598 s->beams_right_i_ = b[i+1];
599 multiple_i_ = multiple_i_ >? (b[i] >? b[i+1]);
604 beams to go with one stem.
607 Beam::stem_beams (Stem *here, Stem *next, Stem *prev) const
609 assert (!next || next->hpos_f () > here->hpos_f ());
610 assert (!prev || prev->hpos_f () < here->hpos_f ());
612 Real staffline_f = paper ()->rule_thickness ();
613 Real interbeam_f = paper ()->interbeam_f (multiple_i_);
614 Real internote_f = paper ()->internote_f ();
615 Real beam_f = paper ()->beam_thickness_f ();
617 Real dy = interbeam_f;
618 Real stemdx = staffline_f;
619 Real sl = slope_f_* internote_f;
620 paper ()->lookup_l ()->beam (sl, 20 PT, 1 PT);
625 /* half beams extending to the left. */
628 int lhalfs= lhalfs = here->beams_left_i_ - prev->beams_right_i_ ;
629 int lwholebeams= here->beams_left_i_ <? prev->beams_right_i_ ;
630 Real w = (here->hpos_f () - prev->hpos_f ())/4 <? paper ()->note_width ();;
632 if (lhalfs) // generates warnings if not
633 a = paper ()->lookup_l ()->beam (sl, w, beam_f);
634 a.translate (Offset (-w, -w * sl));
635 for (int j = 0; j < lhalfs; j++)
638 b.translate_axis (-dir_ * dy * (lwholebeams+j), Y_AXIS);
645 int rhalfs = here->beams_right_i_ - next->beams_left_i_;
646 int rwholebeams = here->beams_right_i_ <? next->beams_left_i_;
648 Real w = next->hpos_f () - here->hpos_f ();
649 Atom a = paper ()->lookup_l ()->beam (sl, w + stemdx, beam_f);
650 a.translate_axis( - stemdx/2, X_AXIS);
653 if (here->beam_gap_i_)
655 int nogap = rwholebeams - here->beam_gap_i_;
656 for (; j < nogap; j++)
659 b.translate_axis (-dir_ * dy * j, Y_AXIS);
662 // TODO: notehead widths differ for different types
663 gap_f = paper ()->note_width () / 2;
665 a = paper ()->lookup_l ()->beam (sl, w + stemdx, beam_f);
668 for (; j < rwholebeams; j++)
671 b.translate (Offset (gap_f, -dir_ * dy * j));
675 w = w/4 <? paper ()->note_width ();
677 a = paper ()->lookup_l ()->beam (sl, w, beam_f);
679 for (; j < rwholebeams + rhalfs; j++)
682 b.translate_axis (-dir_ * dy * j, Y_AXIS);
687 leftbeams.add (rightbeams);
690 Does beam quanting think of the asymetry of beams?
691 Refpoint is on bottom of symbol. (FIXTHAT) --hwn.