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>
20 The relationship Stem <-> Beam is way too hairy. Let's figure who
21 needs what, and what information should be available when.
28 #include "dimensions.hh"
32 #include "molecule.hh"
33 #include "leastsquares.hh"
35 #include "paper-def.hh"
37 #include "rhythmic-grouping.hh"
43 quantisation_ = NORMAL;
48 Beam::add_stem (Stem*s)
53 dim_cache_[Y_AXIS].parent_l_ = &s->dim_cache_[Y_AXIS];
57 s->add_dependency (this);
60 if (!spanned_drul_[LEFT])
67 Beam::get_stem_info (Stem *s)
70 for (int i=0; i < sinfo_.size (); i++)
72 if (sinfo_[i].stem_l_ == s)
80 Beam::do_brew_molecule_p () const
82 Molecule *mol_p = new Molecule;
86 Real x0 = stems_[0]->hpos_f ();
87 for (int j=0; j <stems_.size (); j++)
90 Stem * prev = (j > 0)? stems_[j-1] : 0;
91 Stem * next = (j < stems_.size ()-1) ? stems_[j+1] :0;
93 Molecule sb = stem_beams (i, next, prev);
94 Real x = i->hpos_f ()-x0;
95 sb.translate (Offset (x, (x * slope_f_ + left_y_) *
96 i->staff_line_leading_f ()/2 ));
97 mol_p->add_molecule (sb);
99 mol_p->translate_axis (x0
100 - spanned_drul_[LEFT]->absolute_coordinate (X_AXIS), X_AXIS);
102 // correct if last note (and therefore reference point of beam)
103 // is on different staff
104 Stem_info si = sinfo_.top ();
105 mol_p->translate_axis (-si.interstaff_f_ * si.stem_l_->staff_line_leading_f ()/2,
112 Beam::center () const
114 Stem_info si = sinfo_[0];
116 Real w= (si.stem_l_->note_delta_f () + extent (X_AXIS).length ())/2.0;
117 return Offset (w, ( w* slope_f_) *
118 si.stem_l_->staff_line_leading_f ()/2);
122 Beam::do_pre_processing ()
129 Beam::do_print () const
132 DOUT << "slope_f_ " << slope_f_ << "left ypos " << left_y_;
133 Spanner::do_print ();
138 Beam::do_post_processing ()
140 if (stems_.size () < 2)
142 warning (_ ("beam with less than two stems"));
143 set_elt_property (transparent_scm_sym, SCM_BOOL_T);
151 Beam::do_substitute_element_pointer (Score_element*o,Score_element*n)
153 if (Stem * os = dynamic_cast<Stem*> (o))
154 stems_.substitute (os,
155 dynamic_cast<Stem *> (n));
159 Beam::do_width () const
161 return Interval (stems_[0]->hpos_f (),
162 stems_.top ()->hpos_f ());
166 Beam::set_default_dir ()
168 Drul_array<int> total;
169 total[UP] = total[DOWN] = 0;
170 Drul_array<int> count;
171 count[UP] = count[DOWN] = 0;
174 for (int i=0; i <stems_.size (); i++)
177 int current = s->dir_
178 ? (1 + d * s->dir_)/2
179 : s->get_center_distance ((Direction)-d);
187 } while (flip(&d) != DOWN);
190 [Ross] states that the majority of the notes dictates the
191 direction (and not the mean of "center distance")
193 But is that because it really looks better, or because he
194 wants to provide some real simple hands-on rules.
196 We have our doubts, so we simply provide all sensible alternatives.
199 Dir_algorithm a = (Dir_algorithm)rint(paper_l ()->get_var ("beam_dir_algorithm"));
203 dir_ = (count[UP] > count[DOWN]) ? UP : DOWN;
206 // mean centre distance
207 dir_ = (total[UP] > total[DOWN]) ? UP : DOWN;
211 // median centre distance
214 else if (!count[DOWN])
217 dir_ = (total[UP] / count[UP] > total[DOWN] / count[DOWN]) ? UP : DOWN;
221 for (int i=0; i <stems_.size (); i++)
224 s->set_elt_property (beam_dir_scm_sym, gh_int2scm (dir_));
226 SCM force = s->remove_elt_property (dir_forced_scm_sym);
227 if (force == SCM_BOOL_F)
233 See Documentation/tex/fonts.doc
239 assert (sinfo_.size () > 1);
240 DOUT << "Beam::solve_slope: \n";
243 for (int i=0; i < sinfo_.size (); i++)
245 l.input.push (Offset (sinfo_[i].x_, sinfo_[i].idealy_f_));
247 l.minimise (slope_f_, left_y_);
251 Beam::check_stemlengths_f (bool set_b)
253 Real interbeam_f = paper_l ()->interbeam_f (multiple_i_);
255 Real beam_f = paper_l ()->beam_thickness_f ();
256 Real staffline_f = paper_l ()->rule_thickness ();
257 Real epsilon_f = staffline_f / 8;
259 for (int i=0; i < sinfo_.size (); i++)
261 Real y = sinfo_[i].x_ * slope_f_ + left_y_;
264 if (dir_ != sinfo_[i].dir_)
266 Real internote_f = sinfo_[i].stem_l_->staff_line_leading_f ()/2;
267 y -= dir_ * (beam_f / 2
268 + (sinfo_[i].mult_i_ - 1) * interbeam_f) / internote_f;
269 if (!i && sinfo_[i].stem_l_->staff_symbol_l () !=
270 sinfo_.top ().stem_l_->staff_symbol_l ())
271 y += dir_ * (multiple_i_ - (sinfo_[i].stem_l_->flag_i_ - 2) >? 0)
272 * interbeam_f / internote_f;
276 sinfo_[i].stem_l_->set_stemend (y - sinfo_[i].interstaff_f_);
279 if (y > sinfo_[i].maxy_f_)
280 dy_f = dy_f <? sinfo_[i].maxy_f_ - y;
281 if (y < sinfo_[i].miny_f_)
283 // when all too short, normal stems win..
284 if (dy_f < -epsilon_f)
285 warning (_ ("weird beam shift, check your knees"));
286 dy_f = dy_f >? sinfo_[i].miny_f_ - y;
293 Beam::set_steminfo ()
298 assert (multiple_i_);
299 int total_count_i = 0;
300 int forced_count_i = 0;
301 for (int i=0; i < stems_.size (); i++)
305 s->set_default_extents ();
306 if (s->invisible_b ())
308 if (((int)s->chord_start_f ()) && (s->dir_ != s->get_default_dir ()))
313 Real internote_f = stems_[0]->staff_line_leading_f ()/2;
314 int stem_max = (int)rint(paper_l ()->get_var ("stem_max"));
315 Real shorten_f = paper_l ()->get_var (String ("forced_stem_shorten"
316 + to_str (multiple_i_ <? stem_max)))
320 for (int i=0; i < stems_.size (); i++)
323 if (s->invisible_b ())
326 Stem_info info (s, multiple_i_);
330 if (info.dir_ == dir_)
332 if (forced_count_i == total_count_i)
333 info.idealy_f_ -= shorten_f;
334 else if (forced_count_i > total_count_i / 2)
335 info.idealy_f_ -= shorten_f / 2;
342 Beam::calculate_slope ()
346 slope_f_ = left_y_ = 0;
347 else if (sinfo_[0].idealy_f_ == sinfo_.top ().idealy_f_)
350 left_y_ = sinfo_[0].idealy_f_;
356 Real solved_slope_f = slope_f_;
359 steep slope running against lengthened stem is suspect
361 Real dx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
363 // urg, these y internote-y-dimensions
364 Real internote_f = stems_[0]->staff_line_leading_f ()/2;
366 Real lengthened = paper_l ()->get_var ("beam_lengthened") / internote_f;
367 Real steep = paper_l ()->get_var ("beam_steep_slope") / internote_f;
368 if (((left_y_ - sinfo_[0].idealy_f_ > lengthened)
369 && (slope_f_ > steep))
370 || ((left_y_ + slope_f_ * dx_f - sinfo_.top ().idealy_f_ > lengthened)
371 && (slope_f_ < -steep)))
377 This neat trick is by Werner Lemberg,
378 damped = tanh (slope_f_)
379 corresponds with some tables in [Wanske]
381 SCM damp = remove_elt_property (damping_scm_sym);
382 int damping = 1; // ugh.
383 if (damp!= SCM_BOOL_F)
384 damping = gh_int2scm (SCM_CDR(damp));
387 slope_f_ = 0.6 * tanh (slope_f_) / damping;
391 Real damped_slope_dy_f = (solved_slope_f - slope_f_) * dx_f / 2;
392 left_y_ += damped_slope_dy_f;
403 [Ross] (simplification of)
404 Try to set slope_f_ complying with y-span of:
406 - beam_f / 2 + staffline_f / 2
407 - beam_f + staffline_f
411 if (quantisation_ <= NONE)
414 Real interline_f = stems_[0]->staff_line_leading_f ();
415 Real internote_f = interline_f / 2;
416 Real staffline_f = paper_l ()->rule_thickness ();
417 Real beam_f = paper_l ()->beam_thickness_f ();
419 Real dx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
421 // dim(y) = internote; so slope = (y/internote)/x
422 Real dy_f = dx_f * abs (slope_f_ * internote_f);
426 /* UGR. ICE in 2.8.1; bugreport filed. */
427 Array<Real> allowed_fraction (3);
428 allowed_fraction[0] = 0;
429 allowed_fraction[1] = (beam_f / 2 + staffline_f / 2);
430 allowed_fraction[2] = (beam_f + staffline_f);
433 Interval iv = quantise_iv (allowed_fraction, interline_f, dy_f);
434 quanty_f = (dy_f - iv.min () <= iv.max () - dy_f)
439 slope_f_ = (quanty_f / dx_f) / internote_f * sign (slope_f_);
442 static int test_pos = 0;
447 Prevent interference from stafflines and beams. See Documentation/tex/fonts.doc
451 Beam::quantise_left_y (bool extend_b)
454 we only need to quantise the start of the beam as dy is quantised too
455 if extend_b then stems must *not* get shorter
458 if (quantisation_ <= NONE)
462 ----------------------------------------------------------
466 --------------########------------------------------------
469 hang straddle sit inter hang
472 Real space = stems_[0]->staff_line_leading_f ();
473 Real internote_f = space /2;
474 Real staffline_f = paper_l ()->rule_thickness ();
475 Real beam_f = paper_l ()->beam_thickness_f ();
479 it would be nice to have all allowed positions in a runtime matrix:
480 (multiplicity, minimum_beam_dy, maximum_beam_dy)
484 Real sit = beam_f / 2 - staffline_f / 2;
485 Real inter = space / 2;
486 Real hang = space - beam_f / 2 + staffline_f / 2;
489 Put all allowed positions into an array.
490 Whether a position is allowed or not depends on
491 strictness of quantisation, multiplicity and direction.
493 For simplicity, we'll assume dir = UP and correct if
494 dir = DOWN afterwards.
497 // dim(left_y_) = internote
498 Real dy_f = dir_ * left_y_ * internote_f;
500 Real beamdx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
501 Real beamdy_f = beamdx_f * slope_f_ * internote_f;
503 Array<Real> allowed_position;
504 if (quantisation_ != TEST)
506 if (quantisation_ <= NORMAL)
508 if ((multiple_i_ <= 2) || (abs (beamdy_f) >= staffline_f / 2))
509 allowed_position.push (straddle);
510 if ((multiple_i_ <= 1) || (abs (beamdy_f) >= staffline_f / 2))
511 allowed_position.push (sit);
512 allowed_position.push (hang);
515 // TODO: check and fix TRADITIONAL
517 if ((multiple_i_ <= 2) || (abs (beamdy_f) >= staffline_f / 2))
518 allowed_position.push (straddle);
519 if ((multiple_i_ <= 1) && (beamdy_f <= staffline_f / 2))
520 allowed_position.push (sit);
521 if (beamdy_f >= -staffline_f / 2)
522 allowed_position.push (hang);
529 allowed_position.push (hang);
530 cout << "hang" << hang << "\n";
532 else if (test_pos==1)
534 allowed_position.push (straddle);
535 cout << "straddle" << straddle << endl;
537 else if (test_pos==2)
539 allowed_position.push (sit);
540 cout << "sit" << sit << endl;
542 else if (test_pos==3)
544 allowed_position.push (inter);
545 cout << "inter" << inter << endl;
549 Interval iv = quantise_iv (allowed_position, space, dy_f);
551 Real quanty_f = dy_f - iv.min () <= iv.max () - dy_f ? iv.min () : iv.max ();
553 quanty_f = iv.max ();
555 // dim(left_y_) = internote
556 left_y_ = dir_ * quanty_f / internote_f;
560 Beam::set_stemlens ()
562 Real staffline_f = paper_l ()->rule_thickness ();
564 Real epsilon_f = staffline_f / 8;
566 DOUT << "Beam::set_stemlens: \n";
567 Real dy_f = check_stemlengths_f (false);
568 for (int i = 0; i < 2; i++)
570 left_y_ += dy_f * dir_;
571 quantise_left_y (dy_f);
572 dy_f = check_stemlengths_f (true);
573 if (abs (dy_f) <= epsilon_f)
575 DOUT << "Beam::set_stemlens: " << i << " iterations\n";
586 ugh. this is broken and should be rewritten.
590 Beam::set_grouping (Rhythmic_grouping def, Rhythmic_grouping cur)
594 assert (cur.children.size () == stems_.size ());
601 for (int j=0; j <stems_.size (); j++)
605 int f = s->flag_i_ - 2;
610 b= cur.generate_beams (flags, fi);
613 assert (stems_.size () == b.size ()/2);
616 for (int j=0, i=0; i < b.size () && j <stems_.size (); j++)
621 if (s->beams_i_drul_[d] < 0)
622 s->beams_i_drul_[d] = b[i];
624 multiple_i_ = multiple_i_ >? s->beams_i_drul_[d];
626 } while ((flip (&d)) != LEFT);
631 beams to go with one stem.
634 Beam::stem_beams (Stem *here, Stem *next, Stem *prev) const
636 assert (!next || next->hpos_f () > here->hpos_f ());
637 assert (!prev || prev->hpos_f () < here->hpos_f ());
639 Real staffline_f = paper_l ()->rule_thickness ();
640 Real interbeam_f = paper_l ()->interbeam_f (multiple_i_);
642 Real internote_f = here->staff_line_leading_f ()/2;
643 Real beam_f = paper_l ()->beam_thickness_f ();
645 Real dy = interbeam_f;
646 Real stemdx = staffline_f;
647 Real sl = slope_f_* internote_f;
648 lookup_l ()->beam (sl, 20 PT, 1 PT);
654 Real nw_f = paper_l ()->note_width () * 0.8;
656 /* half beams extending to the left. */
659 int lhalfs= lhalfs = here->beams_i_drul_[LEFT] - prev->beams_i_drul_[RIGHT] ;
660 int lwholebeams= here->beams_i_drul_[LEFT] <? prev->beams_i_drul_[RIGHT] ;
662 Half beam should be one note-width,
663 but let's make sure two half-beams never touch
665 Real w = here->hpos_f () - prev->hpos_f ();
668 if (lhalfs) // generates warnings if not
669 a = lookup_l ()->beam (sl, w, beam_f);
670 a.translate (Offset (-w, -w * sl));
671 for (int j = 0; j < lhalfs; j++)
674 b.translate_axis (-dir_ * dy * (lwholebeams+j), Y_AXIS);
675 leftbeams.add_molecule (b);
681 int rhalfs = here->beams_i_drul_[RIGHT] - next->beams_i_drul_[LEFT];
682 int rwholebeams = here->beams_i_drul_[RIGHT] <? next->beams_i_drul_[LEFT];
684 Real w = next->hpos_f () - here->hpos_f ();
685 Molecule a = lookup_l ()->beam (sl, w + stemdx, beam_f);
686 a.translate_axis( - stemdx/2, X_AXIS);
690 SCM gap = get_elt_property (beam_gap_scm_sym);
691 if (gap != SCM_BOOL_F)
693 int gap_i = gh_scm2int (gap);
694 int nogap = rwholebeams - gap_i;
696 for (; j < nogap; j++)
699 b.translate_axis (-dir_ * dy * j, Y_AXIS);
700 rightbeams.add_molecule (b);
702 // TODO: notehead widths differ for different types
705 a = lookup_l ()->beam (sl, w + stemdx, beam_f);
708 for (; j < rwholebeams; j++)
711 b.translate (Offset (gap_f, -dir_ * dy * j));
712 rightbeams.add_molecule (b);
717 a = lookup_l ()->beam (sl, w, beam_f);
719 for (; j < rwholebeams + rhalfs; j++)
722 b.translate_axis (-dir_ * dy * j, Y_AXIS);
723 rightbeams.add_molecule (b);
727 leftbeams.add_molecule (rightbeams);
730 Does beam quanting think of the asymetry of beams?
731 Refpoint is on bottom of symbol. (FIXTHAT) --hwn.