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 (o->is_type_b (Stem::static_name ()))
129 stems_.substitute ((Stem*)o->access_Item (), n? (Stem*) n->access_Item ():0);
133 Beam::do_width () const
135 return Interval (stems_[0]->hpos_f (),
136 stems_.top ()->hpos_f ());
140 Beam::set_default_dir ()
142 Drul_array<int> total;
143 total[UP] = total[DOWN] = 0;
144 Drul_array<int> count;
145 count[UP] = count[DOWN] = 0;
148 for (int i=0; i <stems_.size (); i++)
151 int current = s->dir_
152 ? (1 + d * s->dir_)/2
153 : s->get_center_distance ((Direction)-d);
161 } while (flip(&d) != DOWN);
166 } while (flip(&d) != DOWN);
169 [Ross] states that the majority of the notes dictates the
170 direction (and not the mean of "center distance")
172 But is that because it really looks better, or because he
173 wants to provide some real simple hands-on rules.
175 We have our doubts, so we simply provide all sensible alternatives.
178 Dir_algorithm a = (Dir_algorithm)rint(paper ()->get_var ("beam_dir_algorithm"));
182 dir_ = (count[UP] > count[DOWN]) ? UP : DOWN;
185 // mean centre distance
186 dir_ = (total[UP] > total[DOWN]) ? UP : DOWN;
190 // median centre distance
193 else if (!count[DOWN])
196 dir_ = (total[UP] / count[UP] > total[DOWN] / count[DOWN]) ? UP : DOWN;
200 for (int i=0; i <stems_.size (); i++)
204 if (!s->dir_forced_b_)
210 See Documentation/tex/fonts.doc
216 should use minimum energy formulation (cf linespacing)
219 assert (multiple_i_);
220 Array<Stem_info> sinfo;
221 DOUT << "Beam::solve_slope: \n";
222 for (int j=0; j <stems_.size (); j++)
226 i->mult_i_ = multiple_i_;
227 i->set_default_extents ();
228 if (i->invisible_b ())
235 slope_f_ = left_y_ = 0;
236 else if (sinfo.size () == 1)
239 left_y_ = sinfo[0].idealy_f_;
243 Real leftx = sinfo[0].x_;
245 for (int i=0; i < sinfo.size (); i++)
247 sinfo[i].x_ -= leftx;
248 l.input.push (Offset (sinfo[i].x_, sinfo[i].idealy_f_));
251 l.minimise (slope_f_, left_y_);
255 solved_slope_f_ = dir_ * slope_f_;
258 This neat trick is by Werner Lemberg, damped = tanh (slope_f_) corresponds
259 with some tables in [Wanske]
262 slope_f_ = 0.6 * tanh (slope_f_) / damping_i_;
268 dropping lq for stemlengths solves [d d d] [d g d] "bug..."
270 but may be a bit too crude, and result in lots of
273 perhaps only if slope = 0 ?
276 // left_y_ = sinfo[0].minyf_;
278 if (sinfo.size () >= 1)
280 Real staffline_f = paper ()->rule_thickness ();
281 Real epsilon_f = staffline_f / 8;
282 if (abs (slope_f_) < epsilon_f)
283 left_y_ = (sinfo[0].idealy_f_ + sinfo.top ().idealy_f_) / 2;
286 symmetrical, but results often in having stemlength = minimal
288 left_y_ = sinfo[0].dir_ == dir_ ? sinfo[0].miny_f_ : sinfo[0].maxy_f_;
293 Real dx = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
294 if (sinfo[0].dir_ == sinfo.top ().dir_)
295 left_y_ = sinfo[0].idealy_f_ >? sinfo.top ().idealy_f_ - slope_f_ * dx;
298 left_y_ = sinfo[0].idealy_f_;
304 for (int i=0; i < sinfo.size (); i++)
306 Real y = sinfo[i].x_ * slope_f_ + left_y_;
307 Real my = sinfo[i].miny_f_;
323 [Ross] (simplification of)
324 Try to set slope_f_ complying with y-span of:
326 - beam_f / 2 + staffline_f / 2
327 - beam_f + staffline_f
331 if (quantisation_ <= NONE)
334 Real interline_f = paper ()->interline_f ();
335 Real internote_f = interline_f / 2;
336 Real staffline_f = paper ()->rule_thickness ();
337 Real beam_f = paper ()->beam_thickness_f ();
339 Real dx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
341 // dim(y) = internote; so slope = (y/internote)/x
342 Real dy_f = dx_f * abs (slope_f_ * internote_f);
346 /* UGR. ICE in 2.8.1; bugreport filed. */
347 Array<Real> allowed_fraction (3);
348 allowed_fraction[0] = 0;
349 allowed_fraction[1] = (beam_f / 2 + staffline_f / 2);
350 allowed_fraction[2] = (beam_f + staffline_f);
353 Interval iv = quantise_iv (allowed_fraction, interline_f, dy_f);
354 quanty_f = (dy_f - iv.min () <= iv.max () - dy_f)
359 slope_f_ = (quanty_f / dx_f) / internote_f * sign (slope_f_);
362 static int test_pos = 0;
367 Prevent interference from stafflines and beams. See Documentation/tex/fonts.doc
371 Beam::quantise_left_y (bool extend_b)
374 we only need to quantise the start of the beam as dy is quantised too
375 if extend_b then stems must *not* get shorter
378 if (quantisation_ <= NONE)
382 ----------------------------------------------------------
386 --------------########------------------------------------
389 hang straddle sit inter hang
392 Real interline_f = paper ()->interline_f ();
393 Real internote_f = paper ()->internote_f ();
394 Real staffline_f = paper ()->rule_thickness ();
395 Real beam_f = paper ()->beam_thickness_f ();
399 it would be nice to have all allowed positions in a runtime matrix:
400 (multiplicity, minimum_beam_dy, maximum_beam_dy)
404 Real sit = beam_f / 2 - staffline_f / 2;
405 Real inter = interline_f / 2;
406 Real hang = interline_f - beam_f / 2 + staffline_f / 2;
409 Put all allowed positions into an array.
410 Whether a position is allowed or not depends on
411 strictness of quantisation, multiplicity and direction.
413 For simplicity, we'll assume dir = UP and correct if
414 dir = DOWN afterwards.
417 // dim(left_y_) = internote
418 Real dy_f = dir_ * left_y_ * internote_f;
420 Real beamdx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
421 Real beamdy_f = beamdx_f * slope_f_ * internote_f;
423 Array<Real> allowed_position;
424 if (quantisation_ != TEST)
426 if (quantisation_ <= NORMAL)
428 if ((multiple_i_ <= 2) || (abs (beamdy_f) >= staffline_f / 2))
429 allowed_position.push (straddle);
430 if ((multiple_i_ <= 1) || (abs (beamdy_f) >= staffline_f / 2))
431 allowed_position.push (sit);
432 allowed_position.push (hang);
435 // TODO: check and fix TRADITIONAL
437 if ((multiple_i_ <= 2) || (abs (beamdy_f) >= staffline_f / 2))
438 allowed_position.push (straddle);
439 if ((multiple_i_ <= 1) && (beamdy_f <= staffline_f / 2))
440 allowed_position.push (sit);
441 if (beamdy_f >= -staffline_f / 2)
442 allowed_position.push (hang);
449 allowed_position.push (hang);
450 cout << "hang" << hang << endl;
452 else if (test_pos==1)
454 allowed_position.push (straddle);
455 cout << "straddle" << straddle << endl;
457 else if (test_pos==2)
459 allowed_position.push (sit);
460 cout << "sit" << sit << endl;
462 else if (test_pos==3)
464 allowed_position.push (inter);
465 cout << "inter" << inter << endl;
470 // this currently never happens
471 Real q = (dy_f / interline_f - dy_i) * interline_f;
472 if ((quantisation_ < NORMAL) && (q < interline_f / 3 - beam_f / 2))
473 allowed_position.push (inter);
476 Interval iv = quantise_iv (allowed_position, interline_f, dy_f);
478 Real quanty_f = dy_f - iv.min () <= iv.max () - dy_f ? iv.min () : iv.max ();
480 quanty_f = iv.max ();
482 // dim(left_y_) = internote
483 left_y_ = dir_ * quanty_f / internote_f;
487 Beam::set_stemlens ()
489 Real staffline_f = paper ()->rule_thickness ();
490 Real interbeam_f = paper ()->interbeam_f (multiple_i_);
491 Real internote_f = paper ()->internote_f ();
492 Real beam_f = paper ()->beam_thickness_f ();
495 Real epsilon_f = staffline_f / 8;
499 Damped and quantised slopes, esp. in monotone scales such as
503 will soon produce the minimal stem-length for one of the extreme
504 stems, which is wrong (and ugly). The minimum stemlength should
505 be kept rather small, in order to handle extreme beaming, such as
507 [c c' 'c] %assuming no knee
510 To avoid these short stems for normal cases, we'll correct for
511 the loss in slope, if necessary.
514 ugh, another hack. who's next?
515 Writing this all down, i realise (at last) that the Right Thing to
516 do is to assign uglyness to slope and stem-lengths and then minimise
517 the total uglyness of a beam.
518 Steep slopes are ugly, shortened stems are ugly, lengthened stems
524 Real dx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
525 Real damp_correct_f = paper ()->get_var ("beam_slope_damp_correct_factor");
526 Real damped_slope_dy_f = (solved_slope_f_ - slope_f_) * dx_f
528 damped_slope_dy_f *= damp_correct_f;
529 if (damped_slope_dy_f <= epsilon_f)
530 damped_slope_dy_f = 0;
532 DOUT << "Beam::set_stemlens: \n";
533 Real x0 = stems_[0]->hpos_f ();
536 for (int jj = 0; jj < 10; jj++)
538 left_y_ += dy_f * dir_;
539 quantise_left_y (dy_f);
541 for (int i=0; i < stems_.size (); i++)
544 if (s->transparent_b_)
547 Real x = s->hpos_f () - x0;
548 // urg move this to stem-info
549 Real sy = left_y_ + slope_f_ * x;
551 sy -= dir_ * (beam_f / 2
552 + (s->mult_i_ - 1) * interbeam_f) / internote_f;
554 Real y = s->stem_end_f () * dir_;
556 if (y > info.maxy_f_)
557 dy_f = dy_f <? info.maxy_f_ - y;
558 if (y < info.miny_f_)
560 // when all too short, normal stems win..
561 if (dy_f < -epsilon_f)
562 warning (_ ("weird beam shift, check your knees"));
563 dy_f = dy_f >? info.miny_f_ - y;
566 if (damped_slope_dy_f && (dy_f >= 0))
567 dy_f += damped_slope_dy_f;
568 damped_slope_dy_f = 0;
569 if (abs (dy_f) <= epsilon_f)
571 DOUT << "Beam::set_stemlens: " << jj << " iterations\n";
582 ugh. this is broken and should be rewritten.
586 Beam::set_grouping (Rhythmic_grouping def, Rhythmic_grouping cur)
590 assert (cur.children.size () == stems_.size ());
597 for (int j=0; j <stems_.size (); j++)
601 int f = s->flag_i_ - 2;
606 b= cur.generate_beams (flags, fi);
609 assert (stems_.size () == b.size ()/2);
612 for (int j=0, i=0; i < b.size () && j <stems_.size (); i+= 2, j++)
615 s->beams_left_i_ = b[i];
616 s->beams_right_i_ = b[i+1];
617 multiple_i_ = multiple_i_ >? (b[i] >? b[i+1]);
622 beams to go with one stem.
625 Beam::stem_beams (Stem *here, Stem *next, Stem *prev) const
627 assert (!next || next->hpos_f () > here->hpos_f ());
628 assert (!prev || prev->hpos_f () < here->hpos_f ());
630 Real staffline_f = paper ()->rule_thickness ();
631 Real interbeam_f = paper ()->interbeam_f (multiple_i_);
632 Real internote_f = paper ()->internote_f ();
633 Real beam_f = paper ()->beam_thickness_f ();
635 Real dy = interbeam_f;
636 Real stemdx = staffline_f;
637 Real sl = slope_f_* internote_f;
638 lookup_l ()->beam (sl, 20 PT, 1 PT);
643 /* half beams extending to the left. */
646 int lhalfs= lhalfs = here->beams_left_i_ - prev->beams_right_i_ ;
647 int lwholebeams= here->beams_left_i_ <? prev->beams_right_i_ ;
649 Half beam should be one note-width,
650 but let's make sure two half-beams never touch
652 Real w = here->hpos_f () - prev->hpos_f ();
653 w = w/2 <? paper ()->note_width ();
655 if (lhalfs) // generates warnings if not
656 a = lookup_l ()->beam (sl, w, beam_f);
657 a.translate (Offset (-w, -w * sl));
658 for (int j = 0; j < lhalfs; j++)
661 b.translate_axis (-dir_ * dy * (lwholebeams+j), Y_AXIS);
662 leftbeams.add_atom (b);
668 int rhalfs = here->beams_right_i_ - next->beams_left_i_;
669 int rwholebeams = here->beams_right_i_ <? next->beams_left_i_;
671 Real w = next->hpos_f () - here->hpos_f ();
672 Atom a = lookup_l ()->beam (sl, w + stemdx, beam_f);
673 a.translate_axis( - stemdx/2, X_AXIS);
676 if (here->beam_gap_i_)
678 int nogap = rwholebeams - here->beam_gap_i_;
679 for (; j < nogap; j++)
682 b.translate_axis (-dir_ * dy * j, Y_AXIS);
683 rightbeams.add_atom (b);
685 // TODO: notehead widths differ for different types
686 gap_f = paper ()->note_width () / 2;
688 a = lookup_l ()->beam (sl, w + stemdx, beam_f);
691 for (; j < rwholebeams; j++)
694 b.translate (Offset (gap_f, -dir_ * dy * j));
695 rightbeams.add_atom (b);
698 w = w/2 <? paper ()->note_width ();
700 a = lookup_l ()->beam (sl, w, beam_f);
702 for (; j < rwholebeams + rhalfs; j++)
705 b.translate_axis (-dir_ * dy * j, Y_AXIS);
706 rightbeams.add_atom (b);
710 leftbeams.add_molecule (rightbeams);
713 Does beam quanting think of the asymetry of beams?
714 Refpoint is on bottom of symbol. (FIXTHAT) --hwn.