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.minimise (slope_f_, left_y_);
239 solved_slope_f_ = dir_ * slope_f_;
242 This neat trick is by Werner Lemberg, damped = tanh (slope_f_) corresponds
243 with some tables in [Wanske]
246 slope_f_ = 0.6 * tanh (slope_f_) / damping_i_;
252 dropping lq for stemlengths solves [d d d] [d g d] "bug..."
254 but may be a bit too crude, and result in lots of
257 perhaps only if slope = 0 ?
260 if (abs (slope_f_) < epsilon_f)
261 left_y_ = (sinfo[0].idealy_f_ + sinfo.top ().idealy_f_) / 2;
264 symmetrical, but results often in having stemlength = minimal
266 left_y_ = sinfo[0].dir_ == dir_ ? sinfo[0].miny_f_ : sinfo[0].maxy_f_;
271 Real dx = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
272 if (sinfo[0].dir_ == sinfo.top ().dir_)
273 left_y_ = sinfo[0].idealy_f_ >? sinfo.top ().idealy_f_ - slope_f_ * dx;
276 left_y_ = sinfo[0].idealy_f_;
281 Beam::check_stemlengths_f (Array<Stem_info>& sinfo)
284 find shortest stem and adjust left_y accordingly
287 for (int i=0; i < sinfo.size (); i++)
289 Real y = sinfo[i].x_ * slope_f_ + left_y_;
290 Real my = sinfo[i].miny_f_;
299 Beam::calculate_slope ()
301 Real interline_f = paper ()->interline_f ();
302 Real staffline_f = paper ()->rule_thickness ();
303 Real epsilon_f = staffline_f / 8;
305 assert (multiple_i_);
306 Array<Stem_info> sinfo;
307 for (int i=0; i < stems_.size (); i++)
311 s->mult_i_ = multiple_i_;
312 s->set_default_extents ();
313 if (s->invisible_b ())
321 slope_f_ = left_y_ = 0;
322 else if (sinfo.size () == 1)
325 left_y_ = sinfo[0].idealy_f_;
331 for (int i = 0; i < 3; i++)
335 Real dy = check_stemlengths_f (sinfo);
338 // only consider recalculation if long stem adjustments
339 if (!i && (left_y_ - sinfo[0].idealy_f_ < 0.5 * interline_f))
344 // never allow slope to tilt the other way
345 else if (sign (slope_f_) != sign (s))
349 Real dy = check_stemlengths_f (sinfo);
354 else if (abs (slope_f_) > abs (s))
357 Real dy = check_stemlengths_f (sinfo);
361 if (abs (dy) < epsilon_f)
376 [Ross] (simplification of)
377 Try to set slope_f_ complying with y-span of:
379 - beam_f / 2 + staffline_f / 2
380 - beam_f + staffline_f
384 if (quantisation_ <= NONE)
387 Real interline_f = paper ()->interline_f ();
388 Real internote_f = interline_f / 2;
389 Real staffline_f = paper ()->rule_thickness ();
390 Real beam_f = paper ()->beam_thickness_f ();
392 Real dx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
394 // dim(y) = internote; so slope = (y/internote)/x
395 Real dy_f = dx_f * abs (slope_f_ * internote_f);
399 /* UGR. ICE in 2.8.1; bugreport filed. */
400 Array<Real> allowed_fraction (3);
401 allowed_fraction[0] = 0;
402 allowed_fraction[1] = (beam_f / 2 + staffline_f / 2);
403 allowed_fraction[2] = (beam_f + staffline_f);
406 Interval iv = quantise_iv (allowed_fraction, interline_f, dy_f);
407 quanty_f = (dy_f - iv.min () <= iv.max () - dy_f)
412 slope_f_ = (quanty_f / dx_f) / internote_f * sign (slope_f_);
415 static int test_pos = 0;
420 Prevent interference from stafflines and beams. See Documentation/tex/fonts.doc
424 Beam::quantise_left_y (bool extend_b)
427 we only need to quantise the start of the beam as dy is quantised too
428 if extend_b then stems must *not* get shorter
431 if (quantisation_ <= NONE)
435 ----------------------------------------------------------
439 --------------########------------------------------------
442 hang straddle sit inter hang
445 Real interline_f = paper ()->interline_f ();
446 Real internote_f = paper ()->internote_f ();
447 Real staffline_f = paper ()->rule_thickness ();
448 Real beam_f = paper ()->beam_thickness_f ();
452 it would be nice to have all allowed positions in a runtime matrix:
453 (multiplicity, minimum_beam_dy, maximum_beam_dy)
457 Real sit = beam_f / 2 - staffline_f / 2;
458 Real inter = interline_f / 2;
459 Real hang = interline_f - beam_f / 2 + staffline_f / 2;
462 Put all allowed positions into an array.
463 Whether a position is allowed or not depends on
464 strictness of quantisation, multiplicity and direction.
466 For simplicity, we'll assume dir = UP and correct if
467 dir = DOWN afterwards.
470 // dim(left_y_) = internote
471 Real dy_f = dir_ * left_y_ * internote_f;
473 Real beamdx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
474 Real beamdy_f = beamdx_f * slope_f_ * internote_f;
476 Array<Real> allowed_position;
477 if (quantisation_ != TEST)
479 if (quantisation_ <= NORMAL)
481 if ((multiple_i_ <= 2) || (abs (beamdy_f) >= staffline_f / 2))
482 allowed_position.push (straddle);
483 if ((multiple_i_ <= 1) || (abs (beamdy_f) >= staffline_f / 2))
484 allowed_position.push (sit);
485 allowed_position.push (hang);
488 // TODO: check and fix TRADITIONAL
490 if ((multiple_i_ <= 2) || (abs (beamdy_f) >= staffline_f / 2))
491 allowed_position.push (straddle);
492 if ((multiple_i_ <= 1) && (beamdy_f <= staffline_f / 2))
493 allowed_position.push (sit);
494 if (beamdy_f >= -staffline_f / 2)
495 allowed_position.push (hang);
502 allowed_position.push (hang);
503 cout << "hang" << hang << endl;
505 else if (test_pos==1)
507 allowed_position.push (straddle);
508 cout << "straddle" << straddle << endl;
510 else if (test_pos==2)
512 allowed_position.push (sit);
513 cout << "sit" << sit << endl;
515 else if (test_pos==3)
517 allowed_position.push (inter);
518 cout << "inter" << inter << endl;
523 // this currently never happens
524 Real q = (dy_f / interline_f - dy_i) * interline_f;
525 if ((quantisation_ < NORMAL) && (q < interline_f / 3 - beam_f / 2))
526 allowed_position.push (inter);
529 Interval iv = quantise_iv (allowed_position, interline_f, dy_f);
531 Real quanty_f = dy_f - iv.min () <= iv.max () - dy_f ? iv.min () : iv.max ();
533 quanty_f = iv.max ();
535 // dim(left_y_) = internote
536 left_y_ = dir_ * quanty_f / internote_f;
540 Beam::set_stemlens ()
542 Real staffline_f = paper ()->rule_thickness ();
543 Real interbeam_f = paper ()->interbeam_f (multiple_i_);
544 Real internote_f = paper ()->internote_f ();
545 Real beam_f = paper ()->beam_thickness_f ();
548 Real epsilon_f = staffline_f / 8;
552 Damped and quantised slopes, esp. in monotone scales such as
556 will soon produce the minimal stem-length for one of the extreme
557 stems, which is wrong (and ugly). The minimum stemlength should
558 be kept rather small, in order to handle extreme beaming, such as
560 [c c' 'c] %assuming no knee
563 To avoid these short stems for normal cases, we'll correct for
564 the loss in slope, if necessary.
567 ugh, another hack. who's next?
568 Writing this all down, i realise (at last) that the Right Thing to
569 do is to assign uglyness to slope and stem-lengths and then minimise
570 the total uglyness of a beam.
571 Steep slopes are ugly, shortened stems are ugly, lengthened stems
577 Real dx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
578 Real damp_correct_f = paper ()->get_var ("beam_slope_damp_correct_factor");
579 Real damped_slope_dy_f = (solved_slope_f_ - slope_f_) * dx_f
581 damped_slope_dy_f *= damp_correct_f;
582 if (damped_slope_dy_f <= epsilon_f)
583 damped_slope_dy_f = 0;
585 DOUT << "Beam::set_stemlens: \n";
586 Real x0 = stems_[0]->hpos_f ();
589 for (int jj = 0; jj < 10; jj++)
591 left_y_ += dy_f * dir_;
592 quantise_left_y (dy_f);
594 for (int i=0; i < stems_.size (); i++)
597 if (s->transparent_b_)
600 Real x = s->hpos_f () - x0;
601 // urg move this to stem-info
602 Real sy = left_y_ + slope_f_ * x;
604 sy -= dir_ * (beam_f / 2
605 + (s->mult_i_ - 1) * interbeam_f) / internote_f;
607 Real y = s->stem_end_f () * dir_;
609 if (y > info.maxy_f_)
610 dy_f = dy_f <? info.maxy_f_ - y;
611 if (y < info.miny_f_)
613 // when all too short, normal stems win..
614 if (dy_f < -epsilon_f)
615 warning (_ ("weird beam shift, check your knees"));
616 dy_f = dy_f >? info.miny_f_ - y;
619 if (damped_slope_dy_f && (dy_f >= 0))
620 dy_f += damped_slope_dy_f;
621 damped_slope_dy_f = 0;
622 if (abs (dy_f) <= epsilon_f)
624 DOUT << "Beam::set_stemlens: " << jj << " iterations\n";
635 ugh. this is broken and should be rewritten.
639 Beam::set_grouping (Rhythmic_grouping def, Rhythmic_grouping cur)
643 assert (cur.children.size () == stems_.size ());
650 for (int j=0; j <stems_.size (); j++)
654 int f = s->flag_i_ - 2;
659 b= cur.generate_beams (flags, fi);
662 assert (stems_.size () == b.size ()/2);
665 for (int j=0, i=0; i < b.size () && j <stems_.size (); j++)
670 if (s->beams_i_drul_[d] < 0)
671 s->beams_i_drul_[d] = b[i];
673 multiple_i_ = multiple_i_ >? s->beams_i_drul_[d];
675 } while ((flip (&d)) != LEFT);
680 beams to go with one stem.
683 Beam::stem_beams (Stem *here, Stem *next, Stem *prev) const
685 assert (!next || next->hpos_f () > here->hpos_f ());
686 assert (!prev || prev->hpos_f () < here->hpos_f ());
688 Real staffline_f = paper ()->rule_thickness ();
689 Real interbeam_f = paper ()->interbeam_f (multiple_i_);
690 Real internote_f = paper ()->internote_f ();
691 Real beam_f = paper ()->beam_thickness_f ();
693 Real dy = interbeam_f;
694 Real stemdx = staffline_f;
695 Real sl = slope_f_* internote_f;
696 lookup_l ()->beam (sl, 20 PT, 1 PT);
702 Real nw_f = paper ()->note_width () * 0.8;
704 /* half beams extending to the left. */
707 int lhalfs= lhalfs = here->beams_i_drul_[LEFT] - prev->beams_i_drul_[RIGHT] ;
708 int lwholebeams= here->beams_i_drul_[LEFT] <? prev->beams_i_drul_[RIGHT] ;
710 Half beam should be one note-width,
711 but let's make sure two half-beams never touch
713 Real w = here->hpos_f () - prev->hpos_f ();
716 if (lhalfs) // generates warnings if not
717 a = lookup_l ()->beam (sl, w, beam_f);
718 a.translate (Offset (-w, -w * sl));
719 for (int j = 0; j < lhalfs; j++)
722 b.translate_axis (-dir_ * dy * (lwholebeams+j), Y_AXIS);
723 leftbeams.add_atom (b);
729 int rhalfs = here->beams_i_drul_[RIGHT] - next->beams_i_drul_[LEFT];
730 int rwholebeams = here->beams_i_drul_[RIGHT] <? next->beams_i_drul_[LEFT];
732 Real w = next->hpos_f () - here->hpos_f ();
733 Atom a = lookup_l ()->beam (sl, w + stemdx, beam_f);
734 a.translate_axis( - stemdx/2, X_AXIS);
737 if (here->beam_gap_i_)
739 int nogap = rwholebeams - here->beam_gap_i_;
740 for (; j < nogap; j++)
743 b.translate_axis (-dir_ * dy * j, Y_AXIS);
744 rightbeams.add_atom (b);
746 // TODO: notehead widths differ for different types
749 a = lookup_l ()->beam (sl, w + stemdx, beam_f);
752 for (; j < rwholebeams; j++)
755 b.translate (Offset (gap_f, -dir_ * dy * j));
756 rightbeams.add_atom (b);
761 a = lookup_l ()->beam (sl, w, beam_f);
763 for (; j < rwholebeams + rhalfs; j++)
766 b.translate_axis (-dir_ * dy * j, Y_AXIS);
767 rightbeams.add_atom (b);
771 leftbeams.add_molecule (rightbeams);
774 Does beam quanting think of the asymetry of beams?
775 Refpoint is on bottom of symbol. (FIXTHAT) --hwn.