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"
43 quantisation_ = NORMAL;
48 Beam::add_stem (Stem*s)
51 s->add_dependency (this);
54 if (!spanned_drul_[LEFT])
61 Beam::do_brew_molecule_p () const
63 Molecule *mol_p = new Molecule;
64 Real internote_f = paper ()->internote_f ();
66 Real x0 = stems_[0]->hpos_f ();
67 for (int j=0; j <stems_.size (); j++)
70 Stem * prev = (j > 0)? stems_[j-1] : 0;
71 Stem * next = (j < stems_.size ()-1) ? stems_[j+1] :0;
73 Molecule sb = stem_beams (i, next, prev);
74 Real x = i->hpos_f ()-x0;
75 sb.translate (Offset (x, (x * slope_f_ + left_y_) * internote_f));
76 mol_p->add_molecule (sb);
78 mol_p->translate_axis (x0
79 - spanned_drul_[LEFT]->absolute_coordinate (X_AXIS), X_AXIS);
87 Real w= (paper ()->note_width () + extent (X_AXIS).length ())/2.0;
88 return Offset (w, (left_y_ + w* slope_f_)*paper ()->internote_f ());
92 Beam::do_pre_processing ()
99 Beam::do_print () const
102 DOUT << "slope_f_ " << slope_f_ << "left ypos " << left_y_;
103 Spanner::do_print ();
108 Beam::do_post_processing ()
110 if (stems_.size () < 2)
112 warning (_ ("beam with less than two stems"));
113 transparent_b_ = true;
121 Beam::do_substitute_dependent (Score_element*o,Score_element*n)
123 if (Stem * os = dynamic_cast<Stem*> (o))
124 stems_.substitute (os,
125 dynamic_cast<Stem *> (n));
129 Beam::do_width () const
131 return Interval (stems_[0]->hpos_f (),
132 stems_.top ()->hpos_f ());
136 Beam::set_default_dir ()
138 Drul_array<int> total;
139 total[UP] = total[DOWN] = 0;
140 Drul_array<int> count;
141 count[UP] = count[DOWN] = 0;
144 for (int i=0; i <stems_.size (); i++)
147 int current = s->dir_
148 ? (1 + d * s->dir_)/2
149 : s->get_center_distance ((Direction)-d);
157 } while (flip(&d) != DOWN);
161 urg? consider [b''16 a]: will get stem down!
162 i'll leave this 'fix' commented-out in case something breaks.
168 } while (flip(&d) != DOWN);
172 [Ross] states that the majority of the notes dictates the
173 direction (and not the mean of "center distance")
175 But is that because it really looks better, or because he
176 wants to provide some real simple hands-on rules.
178 We have our doubts, so we simply provide all sensible alternatives.
181 Dir_algorithm a = (Dir_algorithm)rint(paper ()->get_var ("beam_dir_algorithm"));
185 dir_ = (count[UP] > count[DOWN]) ? UP : DOWN;
188 // mean centre distance
189 dir_ = (total[UP] > total[DOWN]) ? UP : DOWN;
193 // median centre distance
196 else if (!count[DOWN])
199 dir_ = (total[UP] / count[UP] > total[DOWN] / count[DOWN]) ? UP : DOWN;
203 for (int i=0; i <stems_.size (); i++)
207 if (!s->dir_forced_b_)
213 See Documentation/tex/fonts.doc
217 Beam::solve_slope (Array<Stem_info>& sinfo)
220 should use minimum energy formulation (cf linespacing)
222 assert (sinfo.size () > 1);
223 DOUT << "Beam::solve_slope: \n";
225 Real staffline_f = paper ()->rule_thickness ();
226 Real epsilon_f = staffline_f / 8;
228 Real leftx = sinfo[0].x_;
230 for (int i=0; i < sinfo.size (); i++)
232 sinfo[i].x_ -= leftx;
233 l.input.push (Offset (sinfo[i].x_, sinfo[i].idealy_f_));
236 // l.input[0].y () += left_y_;
237 l.input[0].y () += left_y_ / 2;
238 l.minimise (slope_f_, left_y_);
240 solved_slope_f_ = dir_ * slope_f_;
243 This neat trick is by Werner Lemberg, damped = tanh (slope_f_) corresponds
244 with some tables in [Wanske]
247 slope_f_ = 0.6 * tanh (slope_f_) / damping_i_;
253 dropping lq for stemlengths solves [d d d] [d g d] "bug..."
255 but may be a bit too crude, and result in lots of
258 perhaps only if slope = 0 ?
261 if (abs (slope_f_) < epsilon_f)
262 left_y_ = (sinfo[0].idealy_f_ + sinfo.top ().idealy_f_) / 2;
265 symmetrical, but results often in having stemlength = minimal
267 left_y_ = sinfo[0].dir_ == dir_ ? sinfo[0].miny_f_ : sinfo[0].maxy_f_;
272 Real dx = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
273 if (sinfo[0].dir_ == sinfo.top ().dir_)
274 left_y_ = sinfo[0].idealy_f_ >? sinfo.top ().idealy_f_ - slope_f_ * dx;
277 left_y_ = sinfo[0].idealy_f_;
282 Beam::check_stemlengths_f (Array<Stem_info>& sinfo)
285 find shortest stem and adjust left_y accordingly
288 for (int i=0; i < sinfo.size (); i++)
290 Real y = sinfo[i].x_ * slope_f_ + left_y_;
291 Real my = sinfo[i].miny_f_;
300 Beam::calculate_slope ()
302 Real interline_f = paper ()->interline_f ();
303 Real staffline_f = paper ()->rule_thickness ();
304 Real epsilon_f = staffline_f / 8;
306 assert (multiple_i_);
307 Array<Stem_info> sinfo;
308 for (int i=0; i < stems_.size (); i++)
312 s->mult_i_ = multiple_i_;
313 s->set_default_extents ();
314 if (s->invisible_b ())
322 slope_f_ = left_y_ = 0;
323 else if (sinfo.size () == 1)
326 left_y_ = sinfo[0].idealy_f_;
332 Array <Stem_info> local_sinfo;
334 for (int i = 0; i < 5; i++)
338 Real dy = check_stemlengths_f (sinfo);
341 // only consider recalculation if long stem adjustments
342 if (!i && (left_y_ - sinfo[0].idealy_f_ < 0.5 * interline_f))
347 // never allow slope to tilt the other way
348 else if (sign (slope_f_) != sign (s))
353 Real dy = check_stemlengths_f (sinfo);
358 else if (abs (slope_f_) > abs (s))
362 Real dy = check_stemlengths_f (sinfo);
366 if (abs (dy) < epsilon_f)
381 [Ross] (simplification of)
382 Try to set slope_f_ complying with y-span of:
384 - beam_f / 2 + staffline_f / 2
385 - beam_f + staffline_f
389 if (quantisation_ <= NONE)
392 Real interline_f = paper ()->interline_f ();
393 Real internote_f = interline_f / 2;
394 Real staffline_f = paper ()->rule_thickness ();
395 Real beam_f = paper ()->beam_thickness_f ();
397 Real dx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
399 // dim(y) = internote; so slope = (y/internote)/x
400 Real dy_f = dx_f * abs (slope_f_ * internote_f);
404 /* UGR. ICE in 2.8.1; bugreport filed. */
405 Array<Real> allowed_fraction (3);
406 allowed_fraction[0] = 0;
407 allowed_fraction[1] = (beam_f / 2 + staffline_f / 2);
408 allowed_fraction[2] = (beam_f + staffline_f);
411 Interval iv = quantise_iv (allowed_fraction, interline_f, dy_f);
412 quanty_f = (dy_f - iv.min () <= iv.max () - dy_f)
417 slope_f_ = (quanty_f / dx_f) / internote_f * sign (slope_f_);
420 static int test_pos = 0;
425 Prevent interference from stafflines and beams. See Documentation/tex/fonts.doc
429 Beam::quantise_left_y (bool extend_b)
432 we only need to quantise the start of the beam as dy is quantised too
433 if extend_b then stems must *not* get shorter
436 if (quantisation_ <= NONE)
440 ----------------------------------------------------------
444 --------------########------------------------------------
447 hang straddle sit inter hang
450 Real interline_f = paper ()->interline_f ();
451 Real internote_f = paper ()->internote_f ();
452 Real staffline_f = paper ()->rule_thickness ();
453 Real beam_f = paper ()->beam_thickness_f ();
457 it would be nice to have all allowed positions in a runtime matrix:
458 (multiplicity, minimum_beam_dy, maximum_beam_dy)
462 Real sit = beam_f / 2 - staffline_f / 2;
463 Real inter = interline_f / 2;
464 Real hang = interline_f - beam_f / 2 + staffline_f / 2;
467 Put all allowed positions into an array.
468 Whether a position is allowed or not depends on
469 strictness of quantisation, multiplicity and direction.
471 For simplicity, we'll assume dir = UP and correct if
472 dir = DOWN afterwards.
475 // dim(left_y_) = internote
476 Real dy_f = dir_ * left_y_ * internote_f;
478 Real beamdx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
479 Real beamdy_f = beamdx_f * slope_f_ * internote_f;
481 Array<Real> allowed_position;
482 if (quantisation_ != TEST)
484 if (quantisation_ <= NORMAL)
486 if ((multiple_i_ <= 2) || (abs (beamdy_f) >= staffline_f / 2))
487 allowed_position.push (straddle);
488 if ((multiple_i_ <= 1) || (abs (beamdy_f) >= staffline_f / 2))
489 allowed_position.push (sit);
490 allowed_position.push (hang);
493 // TODO: check and fix TRADITIONAL
495 if ((multiple_i_ <= 2) || (abs (beamdy_f) >= staffline_f / 2))
496 allowed_position.push (straddle);
497 if ((multiple_i_ <= 1) && (beamdy_f <= staffline_f / 2))
498 allowed_position.push (sit);
499 if (beamdy_f >= -staffline_f / 2)
500 allowed_position.push (hang);
507 allowed_position.push (hang);
508 cout << "hang" << hang << endl;
510 else if (test_pos==1)
512 allowed_position.push (straddle);
513 cout << "straddle" << straddle << endl;
515 else if (test_pos==2)
517 allowed_position.push (sit);
518 cout << "sit" << sit << endl;
520 else if (test_pos==3)
522 allowed_position.push (inter);
523 cout << "inter" << inter << endl;
528 // this currently never happens
529 Real q = (dy_f / interline_f - dy_i) * interline_f;
530 if ((quantisation_ < NORMAL) && (q < interline_f / 3 - beam_f / 2))
531 allowed_position.push (inter);
534 Interval iv = quantise_iv (allowed_position, interline_f, dy_f);
536 Real quanty_f = dy_f - iv.min () <= iv.max () - dy_f ? iv.min () : iv.max ();
538 quanty_f = iv.max ();
540 // dim(left_y_) = internote
541 left_y_ = dir_ * quanty_f / internote_f;
545 Beam::set_stemlens ()
547 Real staffline_f = paper ()->rule_thickness ();
548 Real interbeam_f = paper ()->interbeam_f (multiple_i_);
549 Real internote_f = paper ()->internote_f ();
550 Real beam_f = paper ()->beam_thickness_f ();
553 Real epsilon_f = staffline_f / 8;
557 Damped and quantised slopes, esp. in monotone scales such as
561 will soon produce the minimal stem-length for one of the extreme
562 stems, which is wrong (and ugly). The minimum stemlength should
563 be kept rather small, in order to handle extreme beaming, such as
565 [c c' 'c] %assuming no knee
568 To avoid these short stems for normal cases, we'll correct for
569 the loss in slope, if necessary.
572 ugh, another hack. who's next?
573 Writing this all down, i realise (at last) that the Right Thing to
574 do is to assign uglyness to slope and stem-lengths and then minimise
575 the total uglyness of a beam.
576 Steep slopes are ugly, shortened stems are ugly, lengthened stems
582 Real dx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
583 Real damp_correct_f = paper ()->get_var ("beam_slope_damp_correct_factor");
584 Real damped_slope_dy_f = (solved_slope_f_ - slope_f_) * dx_f
586 damped_slope_dy_f *= damp_correct_f;
587 if (damped_slope_dy_f <= epsilon_f)
588 damped_slope_dy_f = 0;
590 DOUT << "Beam::set_stemlens: \n";
591 Real x0 = stems_[0]->hpos_f ();
594 for (int jj = 0; jj < 10; jj++)
596 left_y_ += dy_f * dir_;
597 quantise_left_y (dy_f);
599 for (int i=0; i < stems_.size (); i++)
602 if (s->transparent_b_)
605 Real x = s->hpos_f () - x0;
606 // urg move this to stem-info
607 Real sy = left_y_ + slope_f_ * x;
609 sy -= dir_ * (beam_f / 2
610 + (s->mult_i_ - 1) * interbeam_f) / internote_f;
612 Real y = s->stem_end_f () * dir_;
614 if (y > info.maxy_f_)
615 dy_f = dy_f <? info.maxy_f_ - y;
616 if (y < info.miny_f_)
618 // when all too short, normal stems win..
619 if (dy_f < -epsilon_f)
620 warning (_ ("weird beam shift, check your knees"));
621 dy_f = dy_f >? info.miny_f_ - y;
624 if (damped_slope_dy_f && (dy_f >= 0))
625 dy_f += damped_slope_dy_f;
626 damped_slope_dy_f = 0;
627 if (abs (dy_f) <= epsilon_f)
629 DOUT << "Beam::set_stemlens: " << jj << " iterations\n";
640 ugh. this is broken and should be rewritten.
644 Beam::set_grouping (Rhythmic_grouping def, Rhythmic_grouping cur)
648 assert (cur.children.size () == stems_.size ());
655 for (int j=0; j <stems_.size (); j++)
659 int f = s->flag_i_ - 2;
664 b= cur.generate_beams (flags, fi);
667 assert (stems_.size () == b.size ()/2);
670 for (int j=0, i=0; i < b.size () && j <stems_.size (); j++)
675 if (s->beams_i_drul_[d] < 0)
676 s->beams_i_drul_[d] = b[i];
678 multiple_i_ = multiple_i_ >? s->beams_i_drul_[d];
680 } while ((flip (&d)) != LEFT);
685 beams to go with one stem.
688 Beam::stem_beams (Stem *here, Stem *next, Stem *prev) const
690 assert (!next || next->hpos_f () > here->hpos_f ());
691 assert (!prev || prev->hpos_f () < here->hpos_f ());
693 Real staffline_f = paper ()->rule_thickness ();
694 Real interbeam_f = paper ()->interbeam_f (multiple_i_);
695 Real internote_f = paper ()->internote_f ();
696 Real beam_f = paper ()->beam_thickness_f ();
698 Real dy = interbeam_f;
699 Real stemdx = staffline_f;
700 Real sl = slope_f_* internote_f;
701 lookup_l ()->beam (sl, 20 PT, 1 PT);
707 Real nw_f = paper ()->note_width () * 0.8;
709 /* half beams extending to the left. */
712 int lhalfs= lhalfs = here->beams_i_drul_[LEFT] - prev->beams_i_drul_[RIGHT] ;
713 int lwholebeams= here->beams_i_drul_[LEFT] <? prev->beams_i_drul_[RIGHT] ;
715 Half beam should be one note-width,
716 but let's make sure two half-beams never touch
718 Real w = here->hpos_f () - prev->hpos_f ();
721 if (lhalfs) // generates warnings if not
722 a = lookup_l ()->beam (sl, w, beam_f);
723 a.translate (Offset (-w, -w * sl));
724 for (int j = 0; j < lhalfs; j++)
727 b.translate_axis (-dir_ * dy * (lwholebeams+j), Y_AXIS);
728 leftbeams.add_atom (b);
734 int rhalfs = here->beams_i_drul_[RIGHT] - next->beams_i_drul_[LEFT];
735 int rwholebeams = here->beams_i_drul_[RIGHT] <? next->beams_i_drul_[LEFT];
737 Real w = next->hpos_f () - here->hpos_f ();
738 Atom a = lookup_l ()->beam (sl, w + stemdx, beam_f);
739 a.translate_axis( - stemdx/2, X_AXIS);
742 if (here->beam_gap_i_)
744 int nogap = rwholebeams - here->beam_gap_i_;
745 for (; j < nogap; j++)
748 b.translate_axis (-dir_ * dy * j, Y_AXIS);
749 rightbeams.add_atom (b);
751 // TODO: notehead widths differ for different types
754 a = lookup_l ()->beam (sl, w + stemdx, beam_f);
757 for (; j < rwholebeams; j++)
760 b.translate (Offset (gap_f, -dir_ * dy * j));
761 rightbeams.add_atom (b);
766 a = lookup_l ()->beam (sl, w, beam_f);
768 for (; j < rwholebeams + rhalfs; j++)
771 b.translate_axis (-dir_ * dy * j, Y_AXIS);
772 rightbeams.add_atom (b);
776 leftbeams.add_molecule (rightbeams);
779 Does beam quanting think of the asymetry of beams?
780 Refpoint is on bottom of symbol. (FIXTHAT) --hwn.