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.
29 #include "dimensions.hh"
33 #include "molecule.hh"
34 #include "leastsquares.hh"
36 #include "paper-def.hh"
47 Beam::add_stem (Stem*s)
51 should figure out why this didn't work.
57 set_parent (s, Y_AXIS);
61 s->add_dependency (this);
66 if (!spanned_drul_[LEFT])
73 Beam::get_stem_info (Stem *s)
76 for (int i=0; i < sinfo_.size (); i++)
78 if (sinfo_[i].stem_l_ == s)
86 Beam::do_brew_molecule_p () const
88 Molecule *mol_p = new Molecule;
92 Real x0 = stems_[0]->hpos_f ();
93 for (int j=0; j <stems_.size (); j++)
96 Stem * prev = (j > 0)? stems_[j-1] : 0;
97 Stem * next = (j < stems_.size ()-1) ? stems_[j+1] :0;
99 Molecule sb = stem_beams (i, next, prev);
100 Real x = i->hpos_f ()-x0;
101 sb.translate (Offset (x, (x * slope_f_ + left_y_) *
102 i->staff_line_leading_f ()/2 ));
103 mol_p->add_molecule (sb);
105 mol_p->translate_axis (x0
106 - spanned_drul_[LEFT]->relative_coordinate (0, X_AXIS), X_AXIS);
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 Simplistic auto-knees; only consider vertical gap between two
126 Beam::auto_knee (SCM gap, bool interstaff_b)
130 Real internote_f = stems_[0]->staff_line_leading_f ()/2;
131 if (gap != SCM_UNDEFINED)
133 int auto_gap_i = gh_scm2int (gap);
134 for (int i=1; i < stems_.size (); i++)
136 bool is_b = (bool)(sinfo_[i].interstaff_f_ - sinfo_[i-1].interstaff_f_);
137 int l_y = (int)(stems_[i-1]->chord_start_f () / internote_f)
138 + (int)sinfo_[i-1].interstaff_f_;
139 int r_y = (int)(stems_[i]->chord_start_f () / internote_f)
140 + (int)sinfo_[i].interstaff_f_;
141 int gap_i = r_y - l_y;
144 Forced stem directions are ignored. If you don't want auto-knees,
145 don't set, or unset autoKneeGap/autoInterstaffKneeGap.
147 if ((abs (gap_i) >= auto_gap_i) && (!interstaff_b || is_b))
149 knee_y = (r_y + l_y) / 2;
157 for (int i=0; i < stems_.size (); i++)
159 int y = (int)(stems_[i]->chord_start_f () / internote_f)
160 + (int)sinfo_[i].interstaff_f_;
161 stems_[i]->set_direction ( y < knee_y ? UP : DOWN);
162 stems_[i]->set_elt_property ("dir-forced", SCM_BOOL_T);
171 if (auto_knee (get_elt_property ("auto-interstaff-knee-gap"), true))
174 return auto_knee (get_elt_property ("auto-knee-gap"), false);
179 Beam::do_pre_processing ()
182 urg: it seems that info on whether beam (voice) dir was forced
183 is being junked here?
185 if (!get_direction ())
186 set_direction ( get_default_dir ());
188 set_direction (get_direction ());
192 Beam::do_print () const
195 DEBUG_OUT << "slope_f_ " << slope_f_ << "left ypos " << left_y_;
196 Spanner::do_print ();
201 Beam::do_post_processing ()
203 if (stems_.size () < 2)
205 warning (_ ("beam with less than two stems"));
206 set_elt_property ("transparent", SCM_BOOL_T);
213 if auto-knee did its work, most probably stem directions
214 have changed, so we must recalculate all.
216 set_direction ( get_default_dir ());
217 set_direction (get_direction ());
219 /* auto-knees used to only work for slope = 0
220 anyway, should be able to set slope per beam
221 set_elt_property ("damping", gh_int2scm(1000));
232 Beam::do_substitute_element_pointer (Score_element*o,Score_element*n)
234 if (Stem * os = dynamic_cast<Stem*> (o))
235 stems_.substitute (os,
236 dynamic_cast<Stem *> (n));
240 Beam::do_width () const
242 return Interval (stems_[0]->hpos_f (),
243 stems_.top ()->hpos_f ());
247 Beam::get_default_dir () const
249 Drul_array<int> total;
250 total[UP] = total[DOWN] = 0;
251 Drul_array<int> count;
252 count[UP] = count[DOWN] = 0;
255 for (int i=0; i <stems_.size (); i++)
258 int current = s->get_direction ()
259 ? (1 + d * s->get_direction ())/2
260 : s->get_center_distance ((Direction)-d);
268 } while (flip(&d) != DOWN);
271 [Ross] states that the majority of the notes dictates the
272 direction (and not the mean of "center distance")
274 But is that because it really looks better, or because he wants
275 to provide some real simple hands-on rules?
277 We have our doubts, so we simply provide all sensible alternatives.
279 If dir is not determined: up (see stem::get_default_dir ()) */
281 Direction beam_dir = CENTER;
282 Direction neutral_dir = (Direction)(int)paper_l ()->get_var ("stem_default_neutral_direction");
284 SCM a = get_elt_property ("beam-dir-algorithm");
286 if (a == ly_symbol2scm ("majority")) // should get default from paper.
287 beam_dir = (count[UP] == count[DOWN]) ? neutral_dir
288 : (count[UP] > count[DOWN]) ? UP : DOWN;
289 else if (a == ly_symbol2scm ("mean"))
290 // mean center distance
291 beam_dir = (total[UP] == total[DOWN]) ? neutral_dir
292 : (total[UP] > total[DOWN]) ? UP : DOWN;
293 else if (a == ly_symbol2scm ("median"))
295 // median center distance
296 if (count[DOWN] && count[UP])
298 beam_dir = (total[UP] / count[UP] == total[DOWN] / count[DOWN])
300 : (total[UP] / count[UP] > total[DOWN] / count[DOWN]) ? UP : DOWN;
304 beam_dir = (count[UP] == count[DOWN]) ? neutral_dir
305 : (count[UP] > count[DOWN]) ? UP : DOWN;
313 Beam::set_direction (Direction d)
315 Directional_spanner::set_direction (d);
316 for (int i=0; i <stems_.size (); i++)
319 s->set_elt_property ("beam-dir", gh_int2scm (d));
321 SCM force = s->remove_elt_property ("dir-forced");
322 if (force == SCM_UNDEFINED)
323 s->set_direction ( d);
328 See Documentation/tex/fonts.doc
334 assert (sinfo_.size () > 1);
337 for (int i=0; i < sinfo_.size (); i++)
339 l.input.push (Offset (sinfo_[i].x_, sinfo_[i].idealy_f_));
341 l.minimise (slope_f_, left_y_);
345 ugh. Naming: this doesn't check, but sets as well.
349 Beam::check_stemlengths_f (bool set_b)
351 Real interbeam_f = paper_l ()->interbeam_f (multiple_i_);
353 Real beam_f = gh_scm2double (get_elt_property ("beam-thickness"));
354 Real staffline_f = paper_l ()-> get_var ("stafflinethickness");
355 Real epsilon_f = staffline_f / 8;
357 for (int i=0; i < sinfo_.size (); i++)
359 Real y = sinfo_[i].x_ * slope_f_ + left_y_;
362 if (get_direction () != sinfo_[i].get_direction ())
364 Real internote_f = sinfo_[i].stem_l_->staff_line_leading_f ()/2;
365 y -= get_direction () * (beam_f / 2
366 + (sinfo_[i].mult_i_ - 1) * interbeam_f) / internote_f;
367 if (!i && sinfo_[i].stem_l_->staff_symbol_l () !=
368 sinfo_.top ().stem_l_->staff_symbol_l ())
369 y += get_direction () * (multiple_i_ - (sinfo_[i].stem_l_->flag_i_ - 2) >? 0)
370 * interbeam_f / internote_f;
374 sinfo_[i].stem_l_->set_stemend (y - sinfo_[i].interstaff_f_);
376 y *= get_direction ();
377 if (y > sinfo_[i].maxy_f_)
378 dy_f = dy_f <? sinfo_[i].maxy_f_ - y;
379 if (y < sinfo_[i].miny_f_)
381 // when all too short, normal stems win..
382 if (dy_f < -epsilon_f)
383 warning (_ ("weird beam vertical offset"));
384 dy_f = dy_f >? sinfo_[i].miny_f_ - y;
391 Beam::set_steminfo ()
396 assert (multiple_i_);
398 int total_count_i = 0;
399 int forced_count_i = 0;
400 for (int i=0; i < stems_.size (); i++)
404 s->set_default_extents ();
405 if (s->invisible_b ())
407 if (((int)s->chord_start_f ()) && (s->get_direction () != s->get_default_dir ()))
412 bool grace_b = get_elt_property ("grace") == SCM_BOOL_T;
413 String type_str = grace_b ? "grace_" : "";
414 int stem_max = (int)rint(paper_l ()->get_var ("stem_max"));
415 Real shorten_f = paper_l ()->get_var (type_str + "forced_stem_shorten"
416 + to_str (multiple_i_ <? stem_max));
419 for (int i=0; i < stems_.size (); i++)
423 Chord tremolo needs to beam over invisible stems of wholes
425 SCM trem = get_elt_property ("chord-tremolo");
426 if (gh_boolean_p (trem) && gh_scm2bool (trem))
428 if (s->invisible_b ())
432 Stem_info info (s, multiple_i_);
436 if (info.get_direction () == get_direction ())
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_;
456 left_y_ *= get_direction ();
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;
499 left_y_ *= get_direction ();
500 slope_f_ *= get_direction ();
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 == ly_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 = gh_scm2double (get_elt_property ("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 = gh_scm2double (get_elt_property ("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 = get_direction () * 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 == ly_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 == ly_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_ = get_direction () * 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 * get_direction ();
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 = gh_scm2double (get_elt_property ("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 (-get_direction () * 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 (-get_direction () * 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, -get_direction () * dy * j));
788 b.translate (Offset (0, -get_direction () * 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 (-get_direction () * 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.