2 beam.cc -- implement Beam
4 source file of the GNU LilyPond music typesetter
6 (c) 1997--1999, 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 "rhythmic-grouping.hh"
42 quantisation_ = NORMAL;
44 vertical_align_drul_[MIN] = 0;
45 vertical_align_drul_[MAX] = -1;
49 Beam::add_stem (Stem*s)
52 s->add_dependency (this);
55 if (!spanned_drul_[LEFT])
62 Beam::do_brew_molecule_p () const
64 Molecule *mol_p = new Molecule;
65 Real x0 = stems_[0]->hpos_f ();
66 for (int j=0; j <stems_.size (); j++)
69 Stem * prev = (j > 0)? stems_[j-1] : 0;
70 Stem * next = (j < stems_.size ()-1) ? stems_[j+1] :0;
72 Molecule sb = stem_beams (i, next, prev);
73 Real x = i->hpos_f ()-x0;
74 sb.translate (Offset (x, (x * slope_f_ + left_y_) *
75 i->staff_line_leading_f ()/2 ));
76 mol_p->add_molecule (sb);
78 mol_p->translate_axis (x0
79 - spanned_drul_[LEFT]->absolute_coordinate (X_AXIS), X_AXIS);
81 // correct if last note (and therefore reference point of beam)
82 // is on different staff
83 Stem_info si = sinfo_.top ();
84 mol_p->translate_axis (-si.interstaff_f_ * si.stem_l_->staff_line_leading_f ()/2,
93 Stem_info si = sinfo_[0];
95 Real w= (si.stem_l_->note_delta_f () + extent (X_AXIS).length ())/2.0;
96 return Offset (w, (left_y_ + w* slope_f_) *
97 si.stem_l_->staff_line_leading_f ()/2);
101 Beam::do_pre_processing ()
108 Beam::do_print () const
111 DOUT << "slope_f_ " << slope_f_ << "left ypos " << left_y_;
112 Spanner::do_print ();
117 Beam::do_post_processing ()
119 if (stems_.size () < 2)
121 warning (_ ("beam with less than two stems"));
122 set_elt_property (transparent_scm_sym, SCM_BOOL_T);
130 Beam::do_substitute_element_pointer (Score_element*o,Score_element*n)
132 if (Stem * os = dynamic_cast<Stem*> (o))
133 stems_.substitute (os,
134 dynamic_cast<Stem *> (n));
138 Beam::do_width () const
140 return Interval (stems_[0]->hpos_f (),
141 stems_.top ()->hpos_f ());
145 Beam::set_default_dir ()
147 Drul_array<int> total;
148 total[UP] = total[DOWN] = 0;
149 Drul_array<int> count;
150 count[UP] = count[DOWN] = 0;
153 for (int i=0; i <stems_.size (); i++)
156 int current = s->dir_
157 ? (1 + d * s->dir_)/2
158 : s->get_center_distance ((Direction)-d);
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_l ()->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
217 should use minimum energy formulation (cf linespacing)
219 assert (sinfo_.size () > 1);
220 DOUT << "Beam::solve_slope: \n";
223 for (int i=0; i < sinfo_.size (); i++)
225 l.input.push (Offset (sinfo_[i].x_, sinfo_[i].idealy_f_));
227 l.minimise (slope_f_, left_y_);
231 Beam::check_stemlengths_f (bool set_b)
233 Real interbeam_f = paper_l ()->interbeam_f (multiple_i_);
235 Real beam_f = paper_l ()->beam_thickness_f ();
236 Real staffline_f = paper_l ()->rule_thickness ();
237 Real epsilon_f = staffline_f / 8;
239 for (int i=0; i < sinfo_.size (); i++)
241 Real y = sinfo_[i].x_ * slope_f_ + left_y_;
244 if (dir_ != sinfo_[i].dir_)
246 Real internote_f = sinfo_[i].stem_l_->staff_line_leading_f ()/2;
247 y -= dir_ * (beam_f / 2
248 + (sinfo_[i].mult_i_ - 1) * interbeam_f) / internote_f;
249 if (!i && sinfo_[i].stem_l_->staff_symbol_l () !=
250 sinfo_.top ().stem_l_->staff_symbol_l ())
251 y += dir_ * (multiple_i_ - (sinfo_[i].stem_l_->flag_i_ - 2) >? 0)
252 * interbeam_f / internote_f;
256 sinfo_[i].stem_l_->set_stemend (y - sinfo_[i].interstaff_f_);
259 if (y > sinfo_[i].maxy_f_)
260 dy_f = dy_f <? sinfo_[i].maxy_f_ - y;
261 if (y < sinfo_[i].miny_f_)
263 // when all too short, normal stems win..
264 if (dy_f < -epsilon_f)
265 warning (_ ("weird beam shift, check your knees"));
266 dy_f = dy_f >? sinfo_[i].miny_f_ - y;
273 Beam::set_steminfo ()
278 assert (multiple_i_);
279 int total_count_i = 0;
280 int forced_count_i = 0;
281 for (int i=0; i < stems_.size (); i++)
284 s->mult_i_ = multiple_i_;
285 s->set_default_extents ();
286 if (s->invisible_b ())
288 if (((int)s->chord_start_f ()) && (s->dir_ != s->get_default_dir ()))
293 Real internote_f = stems_[0]->staff_line_leading_f ()/2;
294 int stem_max = (int)rint(paper_l ()->get_var ("stem_max"));
295 Real shorten_f = paper_l ()->get_var (String ("forced_stem_shorten"
296 + to_str (multiple_i_ <? stem_max)))
300 for (int i=0; i < stems_.size (); i++)
303 if (s->invisible_b ())
310 if (info.dir_ == dir_)
312 if (forced_count_i == total_count_i)
313 info.idealy_f_ -= shorten_f;
314 else if (forced_count_i > total_count_i / 2)
315 info.idealy_f_ -= shorten_f / 2;
322 Beam::calculate_slope ()
326 slope_f_ = left_y_ = 0;
327 else if (sinfo_[0].idealy_f_ == sinfo_.top ().idealy_f_)
330 left_y_ = sinfo_[0].idealy_f_;
336 Real solved_slope_f = slope_f_;
339 steep slope running against lengthened stem is suspect
341 Real dx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
343 // urg, these y internote-y-dimensions
344 Real internote_f = stems_[0]->staff_line_leading_f ()/2;
346 Real lengthened = paper_l ()->get_var ("beam_lengthened") / internote_f;
347 Real steep = paper_l ()->get_var ("beam_steep_slope") / internote_f;
348 if (((left_y_ - sinfo_[0].idealy_f_ > lengthened)
349 && (slope_f_ > steep))
350 || ((left_y_ + slope_f_ * dx_f - sinfo_.top ().idealy_f_ > lengthened)
351 && (slope_f_ < -steep)))
357 This neat trick is by Werner Lemberg,
358 damped = tanh (slope_f_)
359 corresponds with some tables in [Wanske]
362 slope_f_ = 0.6 * tanh (slope_f_) / damping_i_;
366 Real damped_slope_dy_f = (solved_slope_f - slope_f_) * dx_f / 2;
367 left_y_ += damped_slope_dy_f;
378 [Ross] (simplification of)
379 Try to set slope_f_ complying with y-span of:
381 - beam_f / 2 + staffline_f / 2
382 - beam_f + staffline_f
386 if (quantisation_ <= NONE)
389 Real interline_f = stems_[0]->staff_line_leading_f ();
390 Real internote_f = interline_f / 2;
391 Real staffline_f = paper_l ()->rule_thickness ();
392 Real beam_f = paper_l ()->beam_thickness_f ();
394 Real dx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
396 // dim(y) = internote; so slope = (y/internote)/x
397 Real dy_f = dx_f * abs (slope_f_ * internote_f);
401 /* UGR. ICE in 2.8.1; bugreport filed. */
402 Array<Real> allowed_fraction (3);
403 allowed_fraction[0] = 0;
404 allowed_fraction[1] = (beam_f / 2 + staffline_f / 2);
405 allowed_fraction[2] = (beam_f + staffline_f);
408 Interval iv = quantise_iv (allowed_fraction, interline_f, dy_f);
409 quanty_f = (dy_f - iv.min () <= iv.max () - dy_f)
414 slope_f_ = (quanty_f / dx_f) / internote_f * sign (slope_f_);
417 static int test_pos = 0;
422 Prevent interference from stafflines and beams. See Documentation/tex/fonts.doc
426 Beam::quantise_left_y (bool extend_b)
429 we only need to quantise the start of the beam as dy is quantised too
430 if extend_b then stems must *not* get shorter
433 if (quantisation_ <= NONE)
437 ----------------------------------------------------------
441 --------------########------------------------------------
444 hang straddle sit inter hang
447 Real space = stems_[0]->staff_line_leading_f ();
448 Real internote_f = space /2;
449 Real staffline_f = paper_l ()->rule_thickness ();
450 Real beam_f = paper_l ()->beam_thickness_f ();
454 it would be nice to have all allowed positions in a runtime matrix:
455 (multiplicity, minimum_beam_dy, maximum_beam_dy)
459 Real sit = beam_f / 2 - staffline_f / 2;
460 Real inter = space / 2;
461 Real hang = space - beam_f / 2 + staffline_f / 2;
464 Put all allowed positions into an array.
465 Whether a position is allowed or not depends on
466 strictness of quantisation, multiplicity and direction.
468 For simplicity, we'll assume dir = UP and correct if
469 dir = DOWN afterwards.
472 // dim(left_y_) = internote
473 Real dy_f = dir_ * left_y_ * internote_f;
475 Real beamdx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
476 Real beamdy_f = beamdx_f * slope_f_ * internote_f;
478 Array<Real> allowed_position;
479 if (quantisation_ != TEST)
481 if (quantisation_ <= NORMAL)
483 if ((multiple_i_ <= 2) || (abs (beamdy_f) >= staffline_f / 2))
484 allowed_position.push (straddle);
485 if ((multiple_i_ <= 1) || (abs (beamdy_f) >= staffline_f / 2))
486 allowed_position.push (sit);
487 allowed_position.push (hang);
490 // TODO: check and fix TRADITIONAL
492 if ((multiple_i_ <= 2) || (abs (beamdy_f) >= staffline_f / 2))
493 allowed_position.push (straddle);
494 if ((multiple_i_ <= 1) && (beamdy_f <= staffline_f / 2))
495 allowed_position.push (sit);
496 if (beamdy_f >= -staffline_f / 2)
497 allowed_position.push (hang);
504 allowed_position.push (hang);
505 cout << "hang" << hang << "\n";
507 else if (test_pos==1)
509 allowed_position.push (straddle);
510 cout << "straddle" << straddle << endl;
512 else if (test_pos==2)
514 allowed_position.push (sit);
515 cout << "sit" << sit << endl;
517 else if (test_pos==3)
519 allowed_position.push (inter);
520 cout << "inter" << inter << endl;
524 Interval iv = quantise_iv (allowed_position, space, dy_f);
526 Real quanty_f = dy_f - iv.min () <= iv.max () - dy_f ? iv.min () : iv.max ();
528 quanty_f = iv.max ();
530 // dim(left_y_) = internote
531 left_y_ = dir_ * quanty_f / internote_f;
535 Beam::set_stemlens ()
537 Real staffline_f = paper_l ()->rule_thickness ();
539 Real epsilon_f = staffline_f / 8;
541 DOUT << "Beam::set_stemlens: \n";
542 Real dy_f = check_stemlengths_f (false);
543 for (int i = 0; i < 2; i++)
545 left_y_ += dy_f * dir_;
546 quantise_left_y (dy_f);
547 dy_f = check_stemlengths_f (true);
548 if (abs (dy_f) <= epsilon_f)
550 DOUT << "Beam::set_stemlens: " << i << " iterations\n";
561 ugh. this is broken and should be rewritten.
565 Beam::set_grouping (Rhythmic_grouping def, Rhythmic_grouping cur)
569 assert (cur.children.size () == stems_.size ());
576 for (int j=0; j <stems_.size (); j++)
580 int f = s->flag_i_ - 2;
585 b= cur.generate_beams (flags, fi);
588 assert (stems_.size () == b.size ()/2);
591 for (int j=0, i=0; i < b.size () && j <stems_.size (); j++)
596 if (s->beams_i_drul_[d] < 0)
597 s->beams_i_drul_[d] = b[i];
599 multiple_i_ = multiple_i_ >? s->beams_i_drul_[d];
601 } while ((flip (&d)) != LEFT);
606 beams to go with one stem.
609 Beam::stem_beams (Stem *here, Stem *next, Stem *prev) const
611 assert (!next || next->hpos_f () > here->hpos_f ());
612 assert (!prev || prev->hpos_f () < here->hpos_f ());
614 Real staffline_f = paper_l ()->rule_thickness ();
615 Real interbeam_f = paper_l ()->interbeam_f (multiple_i_);
617 Real internote_f = here->staff_line_leading_f ()/2;
618 Real beam_f = paper_l ()->beam_thickness_f ();
620 Real dy = interbeam_f;
621 Real stemdx = staffline_f;
622 Real sl = slope_f_* internote_f;
623 lookup_l ()->beam (sl, 20 PT, 1 PT);
629 Real nw_f = paper_l ()->note_width () * 0.8;
631 /* half beams extending to the left. */
634 int lhalfs= lhalfs = here->beams_i_drul_[LEFT] - prev->beams_i_drul_[RIGHT] ;
635 int lwholebeams= here->beams_i_drul_[LEFT] <? prev->beams_i_drul_[RIGHT] ;
637 Half beam should be one note-width,
638 but let's make sure two half-beams never touch
640 Real w = here->hpos_f () - prev->hpos_f ();
643 if (lhalfs) // generates warnings if not
644 a = lookup_l ()->beam (sl, w, beam_f);
645 a.translate (Offset (-w, -w * sl));
646 for (int j = 0; j < lhalfs; j++)
649 b.translate_axis (-dir_ * dy * (lwholebeams+j), Y_AXIS);
650 leftbeams.add_molecule (b);
656 int rhalfs = here->beams_i_drul_[RIGHT] - next->beams_i_drul_[LEFT];
657 int rwholebeams = here->beams_i_drul_[RIGHT] <? next->beams_i_drul_[LEFT];
659 Real w = next->hpos_f () - here->hpos_f ();
660 Molecule a = lookup_l ()->beam (sl, w + stemdx, beam_f);
661 a.translate_axis( - stemdx/2, X_AXIS);
664 if (here->beam_gap_i_)
666 int nogap = rwholebeams - here->beam_gap_i_;
667 for (; j < nogap; j++)
670 b.translate_axis (-dir_ * dy * j, Y_AXIS);
671 rightbeams.add_molecule (b);
673 // TODO: notehead widths differ for different types
676 a = lookup_l ()->beam (sl, w + stemdx, beam_f);
679 for (; j < rwholebeams; j++)
682 b.translate (Offset (gap_f, -dir_ * dy * j));
683 rightbeams.add_molecule (b);
688 a = lookup_l ()->beam (sl, w, beam_f);
690 for (; j < rwholebeams + rhalfs; j++)
693 b.translate_axis (-dir_ * dy * j, Y_AXIS);
694 rightbeams.add_molecule (b);
698 leftbeams.add_molecule (rightbeams);
701 Does beam quanting think of the asymetry of beams?
702 Refpoint is on bottom of symbol. (FIXTHAT) --hwn.