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 "chord-tremolo.hh"
30 #include "dimensions.hh"
34 #include "molecule.hh"
35 #include "leastsquares.hh"
37 #include "paper-def.hh"
48 Beam::add_stem (Stem*s)
52 should figure out why this didn't work.
58 set_parent (s, Y_AXIS);
62 s->add_dependency (this);
67 if (!spanned_drul_[LEFT])
74 Beam::get_stem_info (Stem *s)
77 for (int i=0; i < sinfo_.size (); i++)
79 if (sinfo_[i].stem_l_ == s)
87 Beam::do_brew_molecule_p () const
89 Molecule *mol_p = new Molecule;
93 Real x0 = stems_[0]->hpos_f ();
94 for (int j=0; j <stems_.size (); j++)
97 Stem * prev = (j > 0)? stems_[j-1] : 0;
98 Stem * next = (j < stems_.size ()-1) ? stems_[j+1] :0;
100 Molecule sb = stem_beams (i, next, prev);
101 Real x = i->hpos_f ()-x0;
102 sb.translate (Offset (x, (x * slope_f_ + left_y_) *
103 i->staff_line_leading_f ()/2 ));
104 mol_p->add_molecule (sb);
106 mol_p->translate_axis (x0
107 - spanned_drul_[LEFT]->relative_coordinate (0, X_AXIS), X_AXIS);
113 Beam::center () const
115 Stem_info si = sinfo_[0];
117 Real w= (si.stem_l_->note_delta_f () + extent (X_AXIS).length ())/2.0;
118 return Offset (w, ( w* slope_f_) *
119 si.stem_l_->staff_line_leading_f ()/2);
123 Simplistic auto-knees; only consider vertical gap between two
127 Beam::auto_knee (SCM gap, bool interstaff_b)
131 Real internote_f = stems_[0]->staff_line_leading_f ()/2;
132 if (gap != SCM_UNDEFINED)
134 int auto_gap_i = gh_scm2int (gap);
135 for (int i=1; i < stems_.size (); i++)
137 bool is_b = (bool)(sinfo_[i].interstaff_f_ - sinfo_[i-1].interstaff_f_);
138 int l_y = (int)(stems_[i-1]->chord_start_f () / internote_f)
139 + (int)sinfo_[i-1].interstaff_f_;
140 int r_y = (int)(stems_[i]->chord_start_f () / internote_f)
141 + (int)sinfo_[i].interstaff_f_;
142 int gap_i = r_y - l_y;
145 Forced stem directions are ignored. If you don't want auto-knees,
146 don't set, or unset autoKneeGap/autoInterstaffKneeGap.
148 if ((abs (gap_i) >= auto_gap_i) && (!interstaff_b || is_b))
150 knee_y = (r_y + l_y) / 2;
158 for (int i=0; i < stems_.size (); i++)
160 int y = (int)(stems_[i]->chord_start_f () / internote_f)
161 + (int)sinfo_[i].interstaff_f_;
162 stems_[i]->dir_ = y < knee_y ? UP : DOWN;
163 stems_[i]->set_elt_property ("dir-forced", SCM_BOOL_T);
172 if (auto_knee (get_elt_property ("auto-interstaff-knee-gap"), true))
175 return auto_knee (get_elt_property ("auto-knee-gap"), false);
180 Beam::do_pre_processing ()
183 urg: it seems that info on whether beam (voice) dir was forced
184 is being junked here?
187 dir_ = get_default_dir ();
189 set_direction (dir_);
193 Beam::do_print () const
196 DEBUG_OUT << "slope_f_ " << slope_f_ << "left ypos " << left_y_;
197 Spanner::do_print ();
202 Beam::do_post_processing ()
204 if (stems_.size () < 2)
206 warning (_ ("beam with less than two stems"));
207 set_elt_property ("transparent", SCM_BOOL_T);
214 if auto-knee did its work, most probably stem directions
215 have changed, so we must recalculate all.
217 dir_ = get_default_dir ();
218 set_direction (dir_);
220 /* auto-knees used to only work for slope = 0
221 anyway, should be able to set slope per beam
222 set_elt_property ("damping", gh_int2scm(1000));
233 Beam::do_substitute_element_pointer (Score_element*o,Score_element*n)
235 if (Stem * os = dynamic_cast<Stem*> (o))
236 stems_.substitute (os,
237 dynamic_cast<Stem *> (n));
241 Beam::do_width () const
243 return Interval (stems_[0]->hpos_f (),
244 stems_.top ()->hpos_f ());
248 Beam::get_default_dir () const
250 Drul_array<int> total;
251 total[UP] = total[DOWN] = 0;
252 Drul_array<int> count;
253 count[UP] = count[DOWN] = 0;
256 for (int i=0; i <stems_.size (); i++)
259 int current = s->dir_
260 ? (1 + d * s->dir_)/2
261 : s->get_center_distance ((Direction)-d);
269 } while (flip(&d) != DOWN);
272 [Ross] states that the majority of the notes dictates the
273 direction (and not the mean of "center distance")
275 But is that because it really looks better, or because he wants
276 to provide some real simple hands-on rules?
278 We have our doubts, so we simply provide all sensible alternatives.
280 If dir is not determined: up (see stem::get_default_dir ()) */
283 Direction neutral_dir = (Direction)(int)paper_l ()->get_var ("stem_default_neutral_direction");
285 SCM a = get_elt_property ("beam-dir-algorithm");
287 if (a == gh_symbol2scm ("majority")) // should get default from paper.
288 beam_dir = (count[UP] == count[DOWN]) ? neutral_dir
289 : (count[UP] > count[DOWN]) ? UP : DOWN;
290 else if (a == gh_symbol2scm ("mean"))
291 // mean center distance
292 beam_dir = (total[UP] == total[DOWN]) ? neutral_dir
293 : (total[UP] > total[DOWN]) ? UP : DOWN;
294 else if (a == gh_symbol2scm ("median"))
296 // median center distance
297 if (count[DOWN] && count[UP])
299 beam_dir = (total[UP] / count[UP] == total[DOWN] / count[DOWN])
301 : (total[UP] / count[UP] > total[DOWN] / count[DOWN]) ? UP : DOWN;
305 beam_dir = (count[UP] == count[DOWN]) ? neutral_dir
306 : (count[UP] > count[DOWN]) ? UP : DOWN;
314 Beam::set_direction (Direction d)
317 for (int i=0; i <stems_.size (); i++)
320 s->set_elt_property ("beam-dir", gh_int2scm (d));
322 SCM force = s->remove_elt_property ("dir-forced");
323 if (force == SCM_UNDEFINED)
329 See Documentation/tex/fonts.doc
335 assert (sinfo_.size () > 1);
338 for (int i=0; i < sinfo_.size (); i++)
340 l.input.push (Offset (sinfo_[i].x_, sinfo_[i].idealy_f_));
342 l.minimise (slope_f_, left_y_);
346 ugh. Naming: this doesn't check, but sets as well.
350 Beam::check_stemlengths_f (bool set_b)
352 Real interbeam_f = paper_l ()->interbeam_f (multiple_i_);
354 Real beam_f = paper_l ()->get_var ("beam_thickness");;
355 Real staffline_f = paper_l ()-> get_var ("stafflinethickness");
356 Real epsilon_f = staffline_f / 8;
358 for (int i=0; i < sinfo_.size (); i++)
360 Real y = sinfo_[i].x_ * slope_f_ + left_y_;
363 if (dir_ != sinfo_[i].dir_)
365 Real internote_f = sinfo_[i].stem_l_->staff_line_leading_f ()/2;
366 y -= dir_ * (beam_f / 2
367 + (sinfo_[i].mult_i_ - 1) * interbeam_f) / internote_f;
368 if (!i && sinfo_[i].stem_l_->staff_symbol_l () !=
369 sinfo_.top ().stem_l_->staff_symbol_l ())
370 y += dir_ * (multiple_i_ - (sinfo_[i].stem_l_->flag_i_ - 2) >? 0)
371 * interbeam_f / internote_f;
375 sinfo_[i].stem_l_->set_stemend (y - sinfo_[i].interstaff_f_);
378 if (y > sinfo_[i].maxy_f_)
379 dy_f = dy_f <? sinfo_[i].maxy_f_ - y;
380 if (y < sinfo_[i].miny_f_)
382 // when all too short, normal stems win..
383 if (dy_f < -epsilon_f)
384 warning (_ ("weird beam vertical offset"));
385 dy_f = dy_f >? sinfo_[i].miny_f_ - y;
392 Beam::set_steminfo ()
397 assert (multiple_i_);
399 int total_count_i = 0;
400 int forced_count_i = 0;
401 for (int i=0; i < stems_.size (); i++)
405 s->set_default_extents ();
406 if (s->invisible_b ())
408 if (((int)s->chord_start_f ()) && (s->dir_ != s->get_default_dir ()))
413 bool grace_b = get_elt_property ("grace") == SCM_BOOL_T;
414 String type_str = grace_b ? "grace_" : "";
415 int stem_max = (int)rint(paper_l ()->get_var ("stem_max"));
416 Real shorten_f = paper_l ()->get_var (type_str + "forced_stem_shorten"
417 + to_str (multiple_i_ <? stem_max));
420 for (int i=0; i < stems_.size (); i++)
424 Chord tremolo needs to beam over invisible stems of wholes
426 if (!dynamic_cast<Chord_tremolo*> (this))
428 if (s->invisible_b ())
432 Stem_info info (s, multiple_i_);
436 if (info.dir_ == dir_)
438 if (forced_count_i == total_count_i)
439 info.idealy_f_ -= shorten_f;
440 else if (forced_count_i > total_count_i / 2)
441 info.idealy_f_ -= shorten_f / 2;
448 Beam::calculate_slope ()
451 slope_f_ = left_y_ = 0;
452 else if (sinfo_[0].idealy_f_ == sinfo_.top ().idealy_f_)
455 left_y_ = sinfo_[0].idealy_f_;
461 Real solved_slope_f = slope_f_;
464 steep slope running against lengthened stem is suspect
466 Real dx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
468 // urg, these y internote-y-dimensions
469 Real internote_f = stems_[0]->staff_line_leading_f ()/2;
471 Real lengthened = paper_l ()->get_var ("beam_lengthened") / internote_f;
472 Real steep = paper_l ()->get_var ("beam_steep_slope") / internote_f;
473 if (((left_y_ - sinfo_[0].idealy_f_ > lengthened)
474 && (slope_f_ > steep))
475 || ((left_y_ + slope_f_ * dx_f - sinfo_.top ().idealy_f_ > lengthened)
476 && (slope_f_ < -steep)))
482 This neat trick is by Werner Lemberg,
483 damped = tanh (slope_f_)
484 corresponds with some tables in [Wanske]
486 SCM damp = remove_elt_property ("damping");
487 int damping = 1; // ugh.
488 if (damp!= SCM_UNDEFINED)
489 damping = gh_int2scm (damp);
492 slope_f_ = 0.6 * tanh (slope_f_) / damping;
496 Real damped_slope_dy_f = (solved_slope_f - slope_f_) * dx_f / 2;
497 left_y_ += damped_slope_dy_f;
508 [Ross] (simplification of)
509 Try to set slope_f_ complying with y-span of:
511 - beam_f / 2 + staffline_f / 2
512 - beam_f + staffline_f
516 SCM q = get_elt_property ("slope-quantisation");
518 if (q == gh_symbol2scm ("none"))
521 Real interline_f = stems_[0]->staff_line_leading_f ();
522 Real internote_f = interline_f / 2;
523 Real staffline_f = paper_l ()->get_var ("stafflinethickness");
524 Real beam_f = paper_l ()->get_var ("beam_thickness");;
526 Real dx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
528 // dim(y) = internote; so slope = (y/internote)/x
529 Real dy_f = dx_f * abs (slope_f_ * internote_f);
533 Array<Real> allowed_fraction (3);
534 allowed_fraction[0] = 0;
535 allowed_fraction[1] = (beam_f / 2 + staffline_f / 2);
536 allowed_fraction[2] = (beam_f + staffline_f);
539 Interval iv = quantise_iv (allowed_fraction, interline_f, dy_f);
540 quanty_f = (dy_f - iv[SMALLER] <= iv[BIGGER] - dy_f)
545 slope_f_ = (quanty_f / dx_f) / internote_f * sign (slope_f_);
550 Prevent interference from stafflines and beams. See Documentation/tex/fonts.doc
554 Beam::quantise_left_y (bool extend_b)
557 we only need to quantise the start of the beam as dy is quantised too
558 if extend_b then stems must *not* get shorter
560 SCM q = get_elt_property ("slope-quantisation");
564 ----------------------------------------------------------
568 --------------########------------------------------------
571 hang straddle sit inter hang
574 Real space = stems_[0]->staff_line_leading_f ();
575 Real internote_f = space /2;
576 Real staffline_f = paper_l ()->get_var ("stafflinethickness");
577 Real beam_f = paper_l ()->get_var ("beam_thickness");;
581 it would be nice to have all allowed positions in a runtime matrix:
582 (multiplicity, minimum_beam_dy, maximum_beam_dy)
586 Real sit = beam_f / 2 - staffline_f / 2;
587 Real hang = space - beam_f / 2 + staffline_f / 2;
590 Put all allowed positions into an array.
591 Whether a position is allowed or not depends on
592 strictness of quantisation, multiplicity and direction.
594 For simplicity, we'll assume dir = UP and correct if
595 dir = DOWN afterwards.
597 // isn't this asymmetric ? --hwn
599 // dim(left_y_) = internote
600 Real dy_f = dir_ * left_y_ * internote_f;
602 Real beamdx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
603 Real beamdy_f = beamdx_f * slope_f_ * internote_f;
605 Array<Real> allowed_position;
606 if (q == gh_symbol2scm ("normal"))
608 if ((multiple_i_ <= 2) || (abs (beamdy_f) >= staffline_f / 2))
609 allowed_position.push (straddle);
610 if ((multiple_i_ <= 1) || (abs (beamdy_f) >= staffline_f / 2))
611 allowed_position.push (sit);
612 allowed_position.push (hang);
614 else if (q == gh_symbol2scm ("traditional"))
616 // TODO: check and fix TRADITIONAL
617 if ((multiple_i_ <= 2) || (abs (beamdy_f) >= staffline_f / 2))
618 allowed_position.push (straddle);
619 if ((multiple_i_ <= 1) && (beamdy_f <= staffline_f / 2))
620 allowed_position.push (sit);
621 if (beamdy_f >= -staffline_f / 2)
622 allowed_position.push (hang);
626 Interval iv = quantise_iv (allowed_position, space, dy_f);
628 Real quanty_f = dy_f - iv[SMALLER] <= iv[BIGGER] - dy_f ? iv[SMALLER] : iv[BIGGER];
630 quanty_f = iv[BIGGER];
632 // dim(left_y_) = internote
633 left_y_ = dir_ * quanty_f / internote_f;
637 Beam::set_stemlens ()
639 Real staffline_f = paper_l ()->get_var ("stafflinethickness");
641 Real epsilon_f = staffline_f / 8;
644 // je bent zelf eng --hwn.
645 Real dy_f = check_stemlengths_f (false);
646 for (int i = 0; i < 2; i++) // 2 ?
648 left_y_ += dy_f * dir_;
649 quantise_left_y (dy_f);
650 dy_f = check_stemlengths_f (true);
651 if (abs (dy_f) <= epsilon_f)
659 Beam::set_beaming (Beaming_info_list *beaming)
662 for (int i=0; i < stems_.size (); i++)
666 if (stems_[i]->beams_i_drul_[d] < 0)
667 stems_[i]->beams_i_drul_[d] = beaming->infos_.elem (i).beams_i_drul_[d];
669 while (flip (&d) != LEFT);
675 Beam::do_add_processing ()
677 for (int i=0; i < stems_.size () ; i++)
681 multiple_i_ = multiple_i_ >? stems_[i]->beams_i_drul_[d];
682 } while ((flip (&d)) != LEFT);
687 stems_[0]->beams_i_drul_[LEFT] =0;
688 stems_.top()->beams_i_drul_[RIGHT] =0;
695 beams to go with one stem.
700 Beam::stem_beams (Stem *here, Stem *next, Stem *prev) const
702 if ((next && !(next->hpos_f () > here->hpos_f ())) ||
703 (prev && !(prev->hpos_f () < here->hpos_f ())))
704 programming_error ("Beams are not left-to-right");
706 Real staffline_f = paper_l ()->get_var ("stafflinethickness");
707 Real interbeam_f = paper_l ()->interbeam_f (multiple_i_);
709 Real internote_f = here->staff_line_leading_f ()/2;
710 Real beam_f = paper_l ()->get_var ("beam_thickness");;
712 Real dy = interbeam_f;
713 Real stemdx = staffline_f;
714 Real sl = slope_f_* internote_f;
721 if (!here->head_l_arr_.size ())
723 else if (here->type_i ()== 1)
724 nw_f = paper_l ()->get_var ("wholewidth");
725 else if (here->type_i () == 2)
726 nw_f = paper_l ()->get_var ("notewidth") * 0.8;
728 nw_f = paper_l ()->get_var ("quartwidth");
730 /* half beams extending to the left. */
733 int lhalfs= lhalfs = here->beams_i_drul_[LEFT] - prev->beams_i_drul_[RIGHT] ;
734 int lwholebeams= here->beams_i_drul_[LEFT] <? prev->beams_i_drul_[RIGHT] ;
736 Half beam should be one note-width,
737 but let's make sure two half-beams never touch
739 Real w = here->hpos_f () - prev->hpos_f ();
742 if (lhalfs) // generates warnings if not
743 a = lookup_l ()->beam (sl, w, beam_f);
744 a.translate (Offset (-w, -w * sl));
745 for (int j = 0; j < lhalfs; j++)
748 b.translate_axis (-dir_ * dy * (lwholebeams+j), Y_AXIS);
749 leftbeams.add_molecule (b);
755 int rhalfs = here->beams_i_drul_[RIGHT] - next->beams_i_drul_[LEFT];
756 int rwholebeams = here->beams_i_drul_[RIGHT] <? next->beams_i_drul_[LEFT];
758 Real w = next->hpos_f () - here->hpos_f ();
759 Molecule a = lookup_l ()->beam (sl, w + stemdx, beam_f);
760 a.translate_axis( - stemdx/2, X_AXIS);
764 SCM gap = get_elt_property ("beam-gap");
765 if (gap != SCM_UNDEFINED)
767 int gap_i = gh_scm2int ( (gap));
768 int nogap = rwholebeams - gap_i;
770 for (; j < nogap; j++)
773 b.translate_axis (-dir_ * dy * j, Y_AXIS);
774 rightbeams.add_molecule (b);
776 // TODO: notehead widths differ for different types
779 a = lookup_l ()->beam (sl, w + stemdx, beam_f);
782 for (; j < rwholebeams; j++)
785 if (!here->invisible_b ())
786 b.translate (Offset (gap_f, -dir_ * dy * j));
788 b.translate (Offset (0, -dir_ * dy * j));
789 rightbeams.add_molecule (b);
794 a = lookup_l ()->beam (sl, w, beam_f);
796 for (; j < rwholebeams + rhalfs; j++)
799 b.translate_axis (-dir_ * dy * j, Y_AXIS);
800 rightbeams.add_molecule (b);
804 leftbeams.add_molecule (rightbeams);
807 Does beam quanting think of the asymetry of beams?
808 Refpoint is on bottom of symbol. (FIXTHAT) --hwn.