2 beam.cc -- implement Beam
4 source file of the GNU LilyPond music typesetter
6 (c) 1997--1999 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.
27 #include "new-beaming.hh"
29 #include "dimensions.hh"
33 #include "molecule.hh"
34 #include "leastsquares.hh"
36 #include "paper-def.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);
62 if (!spanned_drul_[LEFT])
69 Beam::get_stem_info (Stem *s)
72 for (int i=0; i < sinfo_.size (); i++)
74 if (sinfo_[i].stem_l_ == s)
82 Beam::do_brew_molecule_p () const
84 Molecule *mol_p = new Molecule;
88 Real x0 = stems_[0]->hpos_f ();
89 for (int j=0; j <stems_.size (); j++)
92 Stem * prev = (j > 0)? stems_[j-1] : 0;
93 Stem * next = (j < stems_.size ()-1) ? stems_[j+1] :0;
95 Molecule sb = stem_beams (i, next, prev);
96 Real x = i->hpos_f ()-x0;
97 sb.translate (Offset (x, (x * slope_f_ + left_y_) *
98 i->staff_line_leading_f ()/2 ));
99 mol_p->add_molecule (sb);
101 mol_p->translate_axis (x0
102 - spanned_drul_[LEFT]->absolute_coordinate (X_AXIS), X_AXIS);
108 Beam::center () const
110 Stem_info si = sinfo_[0];
112 Real w= (si.stem_l_->note_delta_f () + extent (X_AXIS).length ())/2.0;
113 return Offset (w, ( w* slope_f_) *
114 si.stem_l_->staff_line_leading_f ()/2);
118 Beam::do_pre_processing ()
121 dir_ = get_default_dir ();
124 set_direction (dir_);
128 Beam::do_print () const
131 DOUT << "slope_f_ " << slope_f_ << "left ypos " << left_y_;
132 Spanner::do_print ();
137 Beam::do_post_processing ()
139 if (stems_.size () < 2)
141 warning (_ ("beam with less than two stems"));
142 set_elt_property (transparent_scm_sym, SCM_BOOL_T);
150 Beam::do_substitute_element_pointer (Score_element*o,Score_element*n)
152 if (Stem * os = dynamic_cast<Stem*> (o))
153 stems_.substitute (os,
154 dynamic_cast<Stem *> (n));
158 Beam::do_width () const
160 return Interval (stems_[0]->hpos_f (),
161 stems_.top ()->hpos_f ());
165 Beam::get_default_dir () const
167 Drul_array<int> total;
168 total[UP] = total[DOWN] = 0;
169 Drul_array<int> count;
170 count[UP] = count[DOWN] = 0;
173 for (int i=0; i <stems_.size (); i++)
176 int current = s->dir_
177 ? (1 + d * s->dir_)/2
178 : s->get_center_distance ((Direction)-d);
186 } while (flip(&d) != DOWN);
189 [Ross] states that the majority of the notes dictates the
190 direction (and not the mean of "center distance")
192 But is that because it really looks better, or because he
193 wants to provide some real simple hands-on rules.
195 We have our doubts, so we simply provide all sensible alternatives.
197 If dir is not determined: up (see stem::get_default_dir ())
201 Direction neutral_dir = (int)paper_l ()->get_var ("stem_default_neutral_direction");
203 Dir_algorithm a = (Dir_algorithm)rint(paper_l ()->get_var ("beam_dir_algorithm"));
207 beam_dir = (count[UP] == count[DOWN]) ? neutral_dir
208 : (count[UP] > count[DOWN]) ? UP : DOWN;
211 // mean center distance
212 beam_dir = (total[UP] == total[DOWN]) ? neutral_dir
213 : (total[UP] > total[DOWN]) ? UP : DOWN;
217 // median center distance
218 if (!count[DOWN] || !count[UP])
220 beam_dir = (count[UP] == count[DOWN]) ? neutral_dir
221 : (count[UP] > count[DOWN]) ? UP : DOWN;
225 beam_dir = (total[UP] / count[UP] == total[DOWN] / count[DOWN])
227 : (total[UP] / count[UP] > total[DOWN] / count[DOWN]) ? UP : DOWN;
235 Beam::set_direction (Direction d)
238 for (int i=0; i <stems_.size (); i++)
241 s->set_elt_property (beam_dir_scm_sym, gh_int2scm (d));
243 SCM force = s->remove_elt_property (dir_forced_scm_sym);
244 if (force == SCM_BOOL_F)
250 See Documentation/tex/fonts.doc
256 assert (sinfo_.size () > 1);
257 DOUT << "Beam::solve_slope: \n";
260 for (int i=0; i < sinfo_.size (); i++)
262 l.input.push (Offset (sinfo_[i].x_, sinfo_[i].idealy_f_));
264 l.minimise (slope_f_, left_y_);
268 ugh. Naming: this doesn't check, but sets as well.
272 Beam::check_stemlengths_f (bool set_b)
274 Real interbeam_f = paper_l ()->interbeam_f (multiple_i_);
276 Real beam_f = paper_l ()->beam_thickness_f ();
277 Real staffline_f = paper_l ()->rule_thickness ();
278 Real epsilon_f = staffline_f / 8;
280 for (int i=0; i < sinfo_.size (); i++)
282 Real y = sinfo_[i].x_ * slope_f_ + left_y_;
285 if (dir_ != sinfo_[i].dir_)
287 Real internote_f = sinfo_[i].stem_l_->staff_line_leading_f ()/2;
288 y -= dir_ * (beam_f / 2
289 + (sinfo_[i].mult_i_ - 1) * interbeam_f) / internote_f;
290 if (!i && sinfo_[i].stem_l_->staff_symbol_l () !=
291 sinfo_.top ().stem_l_->staff_symbol_l ())
292 y += dir_ * (multiple_i_ - (sinfo_[i].stem_l_->flag_i_ - 2) >? 0)
293 * interbeam_f / internote_f;
297 sinfo_[i].stem_l_->set_stemend (y - sinfo_[i].interstaff_f_);
300 if (y > sinfo_[i].maxy_f_)
301 dy_f = dy_f <? sinfo_[i].maxy_f_ - y;
302 if (y < sinfo_[i].miny_f_)
304 // when all too short, normal stems win..
305 if (dy_f < -epsilon_f)
306 warning (_ ("weird beam shift, check your knees"));
307 dy_f = dy_f >? sinfo_[i].miny_f_ - y;
314 Beam::set_steminfo ()
319 assert (multiple_i_);
320 int total_count_i = 0;
321 int forced_count_i = 0;
322 for (int i=0; i < stems_.size (); i++)
326 s->set_default_extents ();
327 if (s->invisible_b ())
329 if (((int)s->chord_start_f ()) && (s->dir_ != s->get_default_dir ()))
334 bool grace_b = get_elt_property (grace_scm_sym) != SCM_BOOL_F;
335 String type_str = grace_b ? "grace_" : "";
336 int stem_max = (int)rint(paper_l ()->get_var ("stem_max"));
337 Real shorten_f = paper_l ()->get_var (type_str + "forced_stem_shorten"
338 + to_str (multiple_i_ <? stem_max));
341 for (int i=0; i < stems_.size (); i++)
345 // abbreviation beam needs to beam over invisible stems of wholes
346 if (s->invisible_b ())
350 Stem_info info (s, multiple_i_);
354 if (info.dir_ == dir_)
356 if (forced_count_i == total_count_i)
357 info.idealy_f_ -= shorten_f;
358 else if (forced_count_i > total_count_i / 2)
359 info.idealy_f_ -= shorten_f / 2;
366 Beam::calculate_slope ()
370 slope_f_ = left_y_ = 0;
371 else if (sinfo_[0].idealy_f_ == sinfo_.top ().idealy_f_)
374 left_y_ = sinfo_[0].idealy_f_;
380 Real solved_slope_f = slope_f_;
383 steep slope running against lengthened stem is suspect
385 Real dx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
387 // urg, these y internote-y-dimensions
388 Real internote_f = stems_[0]->staff_line_leading_f ()/2;
390 Real lengthened = paper_l ()->get_var ("beam_lengthened") / internote_f;
391 Real steep = paper_l ()->get_var ("beam_steep_slope") / internote_f;
392 if (((left_y_ - sinfo_[0].idealy_f_ > lengthened)
393 && (slope_f_ > steep))
394 || ((left_y_ + slope_f_ * dx_f - sinfo_.top ().idealy_f_ > lengthened)
395 && (slope_f_ < -steep)))
401 This neat trick is by Werner Lemberg,
402 damped = tanh (slope_f_)
403 corresponds with some tables in [Wanske]
405 SCM damp = remove_elt_property (damping_scm_sym);
406 int damping = 1; // ugh.
407 if (damp!= SCM_BOOL_F)
408 damping = gh_int2scm (SCM_CDR(damp));
411 slope_f_ = 0.6 * tanh (slope_f_) / damping;
415 Real damped_slope_dy_f = (solved_slope_f - slope_f_) * dx_f / 2;
416 left_y_ += damped_slope_dy_f;
427 [Ross] (simplification of)
428 Try to set slope_f_ complying with y-span of:
430 - beam_f / 2 + staffline_f / 2
431 - beam_f + staffline_f
435 if (quantisation_ <= NONE)
438 Real interline_f = stems_[0]->staff_line_leading_f ();
439 Real internote_f = interline_f / 2;
440 Real staffline_f = paper_l ()->rule_thickness ();
441 Real beam_f = paper_l ()->beam_thickness_f ();
443 Real dx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
445 // dim(y) = internote; so slope = (y/internote)/x
446 Real dy_f = dx_f * abs (slope_f_ * internote_f);
450 /* UGR. ICE in 2.8.1; bugreport filed. */
451 Array<Real> allowed_fraction (3);
452 allowed_fraction[0] = 0;
453 allowed_fraction[1] = (beam_f / 2 + staffline_f / 2);
454 allowed_fraction[2] = (beam_f + staffline_f);
457 Interval iv = quantise_iv (allowed_fraction, interline_f, dy_f);
458 quanty_f = (dy_f - iv[SMALLER] <= iv[BIGGER] - dy_f)
463 slope_f_ = (quanty_f / dx_f) / internote_f * sign (slope_f_);
466 static int test_pos = 0;
471 Prevent interference from stafflines and beams. See Documentation/tex/fonts.doc
475 Beam::quantise_left_y (bool extend_b)
478 we only need to quantise the start of the beam as dy is quantised too
479 if extend_b then stems must *not* get shorter
482 if (quantisation_ <= NONE)
486 ----------------------------------------------------------
490 --------------########------------------------------------
493 hang straddle sit inter hang
496 Real space = stems_[0]->staff_line_leading_f ();
497 Real internote_f = space /2;
498 Real staffline_f = paper_l ()->rule_thickness ();
499 Real beam_f = paper_l ()->beam_thickness_f ();
503 it would be nice to have all allowed positions in a runtime matrix:
504 (multiplicity, minimum_beam_dy, maximum_beam_dy)
508 Real sit = beam_f / 2 - staffline_f / 2;
509 Real inter = space / 2;
510 Real hang = space - beam_f / 2 + staffline_f / 2;
513 Put all allowed positions into an array.
514 Whether a position is allowed or not depends on
515 strictness of quantisation, multiplicity and direction.
517 For simplicity, we'll assume dir = UP and correct if
518 dir = DOWN afterwards.
521 // dim(left_y_) = internote
522 Real dy_f = dir_ * left_y_ * internote_f;
524 Real beamdx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
525 Real beamdy_f = beamdx_f * slope_f_ * internote_f;
527 Array<Real> allowed_position;
528 if (quantisation_ != TEST)
530 if (quantisation_ <= NORMAL)
532 if ((multiple_i_ <= 2) || (abs (beamdy_f) >= staffline_f / 2))
533 allowed_position.push (straddle);
534 if ((multiple_i_ <= 1) || (abs (beamdy_f) >= staffline_f / 2))
535 allowed_position.push (sit);
536 allowed_position.push (hang);
539 // TODO: check and fix TRADITIONAL
541 if ((multiple_i_ <= 2) || (abs (beamdy_f) >= staffline_f / 2))
542 allowed_position.push (straddle);
543 if ((multiple_i_ <= 1) && (beamdy_f <= staffline_f / 2))
544 allowed_position.push (sit);
545 if (beamdy_f >= -staffline_f / 2)
546 allowed_position.push (hang);
553 allowed_position.push (hang);
554 cout << "hang" << hang << "\n";
556 else if (test_pos==1)
558 allowed_position.push (straddle);
559 cout << "straddle" << straddle << endl;
561 else if (test_pos==2)
563 allowed_position.push (sit);
564 cout << "sit" << sit << endl;
566 else if (test_pos==3)
568 allowed_position.push (inter);
569 cout << "inter" << inter << endl;
573 Interval iv = quantise_iv (allowed_position, space, dy_f);
575 Real quanty_f = dy_f - iv[SMALLER] <= iv[BIGGER] - dy_f ? iv[SMALLER] : iv[BIGGER];
577 quanty_f = iv[BIGGER];
579 // dim(left_y_) = internote
580 left_y_ = dir_ * quanty_f / internote_f;
584 Beam::set_stemlens ()
586 Real staffline_f = paper_l ()->rule_thickness ();
588 Real epsilon_f = staffline_f / 8;
591 // je bent zelf eng --hwn.
592 Real dy_f = check_stemlengths_f (false);
593 for (int i = 0; i < 2; i++)
595 left_y_ += dy_f * dir_;
596 quantise_left_y (dy_f);
597 dy_f = check_stemlengths_f (true);
598 if (abs (dy_f) <= epsilon_f)
609 Beam::set_beaming (Beaming_info_list *beaming)
612 for (int i=0; i < stems_.size (); i++)
616 if (stems_[i]->beams_i_drul_[d] < 0)
617 stems_[i]->beams_i_drul_[d] = beaming->infos_.elem (i).beams_i_drul_[d];
619 while (flip (&d) != LEFT);
625 Beam::do_add_processing ()
627 for (int i=0; i < stems_.size () ; i++)
631 multiple_i_ = multiple_i_ >? stems_[i]->beams_i_drul_[d];
632 } while ((flip (&d)) != LEFT);
637 stems_[0]->beams_i_drul_[LEFT] =0;
638 stems_.top()->beams_i_drul_[RIGHT] =0;
645 beams to go with one stem.
648 Beam::stem_beams (Stem *here, Stem *next, Stem *prev) const
650 if ((next && !(next->hpos_f () > here->hpos_f ())) ||
651 (prev && !(prev->hpos_f () < here->hpos_f ())))
652 programming_error ("Beams are not left-to-right");
654 Real staffline_f = paper_l ()->rule_thickness ();
655 Real interbeam_f = paper_l ()->interbeam_f (multiple_i_);
657 Real internote_f = here->staff_line_leading_f ()/2;
658 Real beam_f = paper_l ()->beam_thickness_f ();
660 Real dy = interbeam_f;
661 Real stemdx = staffline_f;
662 Real sl = slope_f_* internote_f;
663 lookup_l ()->beam (sl, 20 PT, 1 PT);
670 if (here->type_i ()== 1)
671 nw_f = paper_l ()->get_var ("wholewidth");
672 else if (here->type_i () == 2)
673 nw_f = paper_l ()->note_width () * 0.8;
675 nw_f = paper_l ()->get_var ("quartwidth");
677 /* half beams extending to the left. */
680 int lhalfs= lhalfs = here->beams_i_drul_[LEFT] - prev->beams_i_drul_[RIGHT] ;
681 int lwholebeams= here->beams_i_drul_[LEFT] <? prev->beams_i_drul_[RIGHT] ;
683 Half beam should be one note-width,
684 but let's make sure two half-beams never touch
686 Real w = here->hpos_f () - prev->hpos_f ();
689 if (lhalfs) // generates warnings if not
690 a = lookup_l ()->beam (sl, w, beam_f);
691 a.translate (Offset (-w, -w * sl));
692 for (int j = 0; j < lhalfs; j++)
695 b.translate_axis (-dir_ * dy * (lwholebeams+j), Y_AXIS);
696 leftbeams.add_molecule (b);
702 int rhalfs = here->beams_i_drul_[RIGHT] - next->beams_i_drul_[LEFT];
703 int rwholebeams = here->beams_i_drul_[RIGHT] <? next->beams_i_drul_[LEFT];
705 Real w = next->hpos_f () - here->hpos_f ();
706 Molecule a = lookup_l ()->beam (sl, w + stemdx, beam_f);
707 a.translate_axis( - stemdx/2, X_AXIS);
711 SCM gap = get_elt_property (beam_gap_scm_sym);
712 if (gap != SCM_BOOL_F)
714 int gap_i = gh_scm2int (SCM_CDR (gap));
715 int nogap = rwholebeams - gap_i;
717 for (; j < nogap; j++)
720 b.translate_axis (-dir_ * dy * j, Y_AXIS);
721 rightbeams.add_molecule (b);
723 // TODO: notehead widths differ for different types
726 a = lookup_l ()->beam (sl, w + stemdx, beam_f);
729 for (; j < rwholebeams; j++)
732 if (!here->invisible_b ())
733 b.translate (Offset (gap_f, -dir_ * dy * j));
735 b.translate (Offset (0, -dir_ * dy * j));
736 rightbeams.add_molecule (b);
741 a = lookup_l ()->beam (sl, w, beam_f);
743 for (; j < rwholebeams + rhalfs; j++)
746 b.translate_axis (-dir_ * dy * j, Y_AXIS);
747 rightbeams.add_molecule (b);
751 leftbeams.add_molecule (rightbeams);
754 Does beam quanting think of the asymetry of beams?
755 Refpoint is on bottom of symbol. (FIXTHAT) --hwn.