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"
38 #include "group-interface.hh"
42 Group_interface g (this, "stems");
51 TODO: Fix this class. This is wildly inefficient.
54 Beam::stem (int i)const
56 return Group_interface__extract_elements ((Beam*) this, (Stem*) 0, "stems")[i];
60 Beam::stem_count ()const
62 Group_interface gi (this, "stems");
68 Beam::add_stem (Stem*s)
70 Group_interface gi (this, "stems");
73 s->add_dependency (this);
75 assert (!s->beam_l ());
76 s->set_elt_property ("beam", self_scm_);
78 if (!spanned_drul_[LEFT])
85 Beam::get_stem_info (Stem *s)
88 for (int i=0; i < sinfo_.size (); i++)
90 if (sinfo_[i].stem_l_ == s)
98 Beam::do_brew_molecule_p () const
100 Molecule *mol_p = new Molecule;
104 Real x0 = stem (0)->hpos_f ();
105 for (int j=0; j <stem_count (); j++)
108 Stem * prev = (j > 0)? stem (j-1) : 0;
109 Stem * next = (j < stem_count ()-1) ? stem (j+1) :0;
111 Molecule sb = stem_beams (i, next, prev);
112 Real x = i->hpos_f ()-x0;
113 sb.translate (Offset (x, (x * slope_f_ + left_y_) *
114 i->staff_line_leading_f ()/2 ));
115 mol_p->add_molecule (sb);
117 mol_p->translate_axis (x0
118 - spanned_drul_[LEFT]->relative_coordinate (0, X_AXIS), X_AXIS);
124 Beam::center () const
126 Stem_info si = sinfo_[0];
128 Real w= (si.stem_l_->note_delta_f () + extent (X_AXIS).length ())/2.0;
129 return Offset (w, ( w* slope_f_) *
130 si.stem_l_->staff_line_leading_f ()/2);
134 Simplistic auto-knees; only consider vertical gap between two
138 Beam::auto_knee (SCM gap, bool interstaff_b)
142 Real internote_f = stem (0)->staff_line_leading_f ()/2;
143 if (gap != SCM_UNDEFINED)
145 int auto_gap_i = gh_scm2int (gap);
146 for (int i=1; i < stem_count (); i++)
148 bool is_b = (bool)(sinfo_[i].interstaff_f_ - sinfo_[i-1].interstaff_f_);
149 int l_y = (int)(stem (i-1)->chord_start_f () / internote_f)
150 + (int)sinfo_[i-1].interstaff_f_;
151 int r_y = (int)(stem (i)->chord_start_f () / internote_f)
152 + (int)sinfo_[i].interstaff_f_;
153 int gap_i = r_y - l_y;
156 Forced stem directions are ignored. If you don't want auto-knees,
157 don't set, or unset autoKneeGap/autoInterstaffKneeGap.
159 if ((abs (gap_i) >= auto_gap_i) && (!interstaff_b || is_b))
161 knee_y = (r_y + l_y) / 2;
169 for (int i=0; i < stem_count (); i++)
171 int y = (int)(stem (i)->chord_start_f () / internote_f)
172 + (int)sinfo_[i].interstaff_f_;
173 stem (i)->set_direction ( y < knee_y ? UP : DOWN);
174 stem (i)->set_elt_property ("dir-forced", SCM_BOOL_T);
183 if (auto_knee (get_elt_property ("auto-interstaff-knee-gap"), true))
186 return auto_knee (get_elt_property ("auto-knee-gap"), false);
191 Beam::do_pre_processing ()
194 urg: it seems that info on whether beam (voice) dir was forced
195 is being junked here?
197 if (!get_direction ())
198 set_direction ( get_default_dir ());
200 set_direction (get_direction ());
204 Beam::do_print () const
207 DEBUG_OUT << "slope_f_ " << slope_f_ << "left ypos " << left_y_;
208 Spanner::do_print ();
213 Beam::do_post_processing ()
215 if (stem_count () < 2)
217 warning (_ ("beam with less than two stems"));
218 set_elt_property ("transparent", SCM_BOOL_T);
225 if auto-knee did its work, most probably stem directions
226 have changed, so we must recalculate all.
228 set_direction ( get_default_dir ());
229 set_direction (get_direction ());
231 /* auto-knees used to only work for slope = 0
232 anyway, should be able to set slope per beam
233 set_elt_property ("damping", gh_int2scm(1000));
246 Beam::do_width () const
248 return Interval (stem (0)->hpos_f (),
249 stems_.top ()->hpos_f ());
254 Beam::get_default_dir () const
256 Drul_array<int> total;
257 total[UP] = total[DOWN] = 0;
258 Drul_array<int> count;
259 count[UP] = count[DOWN] = 0;
262 for (int i=0; i <stem_count (); i++)
265 int current = s->get_direction ()
266 ? (1 + d * s->get_direction ())/2
267 : s->get_center_distance ((Direction)-d);
275 } while (flip(&d) != DOWN);
278 [Ross] states that the majority of the notes dictates the
279 direction (and not the mean of "center distance")
281 But is that because it really looks better, or because he wants
282 to provide some real simple hands-on rules?
284 We have our doubts, so we simply provide all sensible alternatives.
286 If dir is not determined: up (see stem::get_default_dir ()) */
288 Direction beam_dir = CENTER;
289 Direction neutral_dir = (Direction)(int)paper_l ()->get_var ("stem_default_neutral_direction");
291 SCM a = get_elt_property ("beam-dir-algorithm");
293 if (a == ly_symbol2scm ("majority")) // should get default from paper.
294 beam_dir = (count[UP] == count[DOWN]) ? neutral_dir
295 : (count[UP] > count[DOWN]) ? UP : DOWN;
296 else if (a == ly_symbol2scm ("mean"))
297 // mean center distance
298 beam_dir = (total[UP] == total[DOWN]) ? neutral_dir
299 : (total[UP] > total[DOWN]) ? UP : DOWN;
300 else if (a == ly_symbol2scm ("median"))
302 // median center distance
303 if (count[DOWN] && count[UP])
305 beam_dir = (total[UP] / count[UP] == total[DOWN] / count[DOWN])
307 : (total[UP] / count[UP] > total[DOWN] / count[DOWN]) ? UP : DOWN;
311 beam_dir = (count[UP] == count[DOWN]) ? neutral_dir
312 : (count[UP] > count[DOWN]) ? UP : DOWN;
320 Beam::set_direction (Direction d)
322 Directional_spanner::set_direction (d);
323 for (int i=0; i <stem_count (); i++)
326 s->set_elt_property ("beam-dir", gh_int2scm (d));
328 SCM force = s->remove_elt_property ("dir-forced");
329 if (force == SCM_UNDEFINED)
330 s->set_direction ( d);
335 See Documentation/tex/fonts.doc
341 assert (sinfo_.size () > 1);
344 for (int i=0; i < sinfo_.size (); i++)
346 l.input.push (Offset (sinfo_[i].x_, sinfo_[i].idealy_f_));
348 l.minimise (slope_f_, left_y_);
352 ugh. Naming: this doesn't check, but sets as well.
356 Beam::check_stemlengths_f (bool set_b)
358 Real interbeam_f = paper_l ()->interbeam_f (multiple_i_);
360 Real beam_f = gh_scm2double (get_elt_property ("beam-thickness"));
361 Real staffline_f = paper_l ()-> get_var ("stafflinethickness");
362 Real epsilon_f = staffline_f / 8;
364 for (int i=0; i < sinfo_.size (); i++)
366 Real y = sinfo_[i].x_ * slope_f_ + left_y_;
369 if (get_direction () != sinfo_[i].get_direction ())
371 Real internote_f = sinfo_[i].stem_l_->staff_line_leading_f ()/2;
372 y -= get_direction () * (beam_f / 2
373 + (sinfo_[i].mult_i_ - 1) * interbeam_f) / internote_f;
374 if (!i && sinfo_[i].stem_l_->staff_symbol_l () !=
375 sinfo_.top ().stem_l_->staff_symbol_l ())
376 y += get_direction () * (multiple_i_ - (sinfo_[i].stem_l_->flag_i_ - 2) >? 0)
377 * interbeam_f / internote_f;
381 sinfo_[i].stem_l_->set_stemend (y - sinfo_[i].interstaff_f_);
383 y *= get_direction ();
384 if (y > sinfo_[i].maxy_f_)
385 dy_f = dy_f <? sinfo_[i].maxy_f_ - y;
386 if (y < sinfo_[i].miny_f_)
388 // when all too short, normal stems win..
389 if (dy_f < -epsilon_f)
390 warning (_ ("weird beam vertical offset"));
391 dy_f = dy_f >? sinfo_[i].miny_f_ - y;
398 Beam::set_steminfo ()
403 assert (multiple_i_);
405 int total_count_i = 0;
406 int forced_count_i = 0;
407 for (int i=0; i < stem_count (); i++)
411 s->set_default_extents ();
412 if (s->invisible_b ())
414 if (((int)s->chord_start_f ()) && (s->get_direction () != s->get_default_dir ()))
419 bool grace_b = get_elt_property ("grace") == SCM_BOOL_T;
420 String type_str = grace_b ? "grace_" : "";
421 int stem_max = (int)rint(paper_l ()->get_var ("stem_max"));
422 Real shorten_f = paper_l ()->get_var (type_str + "forced_stem_shorten"
423 + to_str (multiple_i_ <? stem_max));
426 for (int i=0; i < stem_count (); i++)
430 Chord tremolo needs to beam over invisible stems of wholes
432 SCM trem = get_elt_property ("chord-tremolo");
433 if (gh_boolean_p (trem) && gh_scm2bool (trem))
435 if (s->invisible_b ())
439 Stem_info info (s, multiple_i_);
443 if (info.get_direction () == get_direction ())
445 if (forced_count_i == total_count_i)
446 info.idealy_f_ -= shorten_f;
447 else if (forced_count_i > total_count_i / 2)
448 info.idealy_f_ -= shorten_f / 2;
455 Beam::calculate_slope ()
458 slope_f_ = left_y_ = 0;
459 else if (sinfo_[0].idealy_f_ == sinfo_.top ().idealy_f_)
462 left_y_ = sinfo_[0].idealy_f_;
463 left_y_ *= get_direction ();
468 Real solved_slope_f = slope_f_;
471 steep slope running against lengthened stem is suspect
473 Real dx_f = stem (stem_count () -1)->hpos_f () - stem (0)->hpos_f ();
475 // urg, these y internote-y-dimensions
476 Real internote_f = stem (0)->staff_line_leading_f ()/2;
478 Real lengthened = paper_l ()->get_var ("beam_lengthened") / internote_f;
479 Real steep = paper_l ()->get_var ("beam_steep_slope") / internote_f;
480 if (((left_y_ - sinfo_[0].idealy_f_ > lengthened)
481 && (slope_f_ > steep))
482 || ((left_y_ + slope_f_ * dx_f - sinfo_.top ().idealy_f_ > lengthened)
483 && (slope_f_ < -steep)))
489 This neat trick is by Werner Lemberg,
490 damped = tanh (slope_f_)
491 corresponds with some tables in [Wanske]
493 SCM damp = remove_elt_property ("damping");
494 int damping = 1; // ugh.
495 if (damp!= SCM_UNDEFINED)
496 damping = gh_int2scm (damp);
499 slope_f_ = 0.6 * tanh (slope_f_) / damping;
503 Real damped_slope_dy_f = (solved_slope_f - slope_f_) * dx_f / 2;
504 left_y_ += damped_slope_dy_f;
506 left_y_ *= get_direction ();
507 slope_f_ *= get_direction ();
515 [Ross] (simplification of)
516 Try to set slope_f_ complying with y-span of:
518 - beam_f / 2 + staffline_f / 2
519 - beam_f + staffline_f
523 SCM q = get_elt_property ("slope-quantisation");
525 if (q == ly_symbol2scm ("none"))
528 Real interline_f = stem (0)->staff_line_leading_f ();
529 Real internote_f = interline_f / 2;
530 Real staffline_f = paper_l ()->get_var ("stafflinethickness");
531 Real beam_f = gh_scm2double (get_elt_property ("beam-thickness"));;
533 Real dx_f = stem (stem_count () -1 )->hpos_f () - stem (0)->hpos_f ();
535 // dim(y) = internote; so slope = (y/internote)/x
536 Real dy_f = dx_f * abs (slope_f_ * internote_f);
540 Array<Real> allowed_fraction (3);
541 allowed_fraction[0] = 0;
542 allowed_fraction[1] = (beam_f / 2 + staffline_f / 2);
543 allowed_fraction[2] = (beam_f + staffline_f);
546 Interval iv = quantise_iv (allowed_fraction, interline_f, dy_f);
547 quanty_f = (dy_f - iv[SMALLER] <= iv[BIGGER] - dy_f)
552 slope_f_ = (quanty_f / dx_f) / internote_f * sign (slope_f_);
557 Prevent interference from stafflines and beams. See Documentation/tex/fonts.doc
561 Beam::quantise_left_y (bool extend_b)
564 we only need to quantise the start of the beam as dy is quantised too
565 if extend_b then stems must *not* get shorter
567 SCM q = get_elt_property ("slope-quantisation");
571 ----------------------------------------------------------
575 --------------########------------------------------------
578 hang straddle sit inter hang
581 Real space = stem (0)->staff_line_leading_f ();
582 Real internote_f = space /2;
583 Real staffline_f = paper_l ()->get_var ("stafflinethickness");
584 Real beam_f = gh_scm2double (get_elt_property ("beam-thickness"));;
588 it would be nice to have all allowed positions in a runtime matrix:
589 (multiplicity, minimum_beam_dy, maximum_beam_dy)
593 Real sit = beam_f / 2 - staffline_f / 2;
594 Real hang = space - beam_f / 2 + staffline_f / 2;
597 Put all allowed positions into an array.
598 Whether a position is allowed or not depends on
599 strictness of quantisation, multiplicity and direction.
601 For simplicity, we'll assume dir = UP and correct if
602 dir = DOWN afterwards.
604 // isn't this asymmetric ? --hwn
606 // dim(left_y_) = internote
607 Real dy_f = get_direction () * left_y_ * internote_f;
609 Real beamdx_f = stem (stem_count () -1)->hpos_f () - stem (0)->hpos_f ();
610 Real beamdy_f = beamdx_f * slope_f_ * internote_f;
612 Array<Real> allowed_position;
613 if (q == ly_symbol2scm ("normal"))
615 if ((multiple_i_ <= 2) || (abs (beamdy_f) >= staffline_f / 2))
616 allowed_position.push (straddle);
617 if ((multiple_i_ <= 1) || (abs (beamdy_f) >= staffline_f / 2))
618 allowed_position.push (sit);
619 allowed_position.push (hang);
621 else if (q == ly_symbol2scm ("traditional"))
623 // TODO: check and fix TRADITIONAL
624 if ((multiple_i_ <= 2) || (abs (beamdy_f) >= staffline_f / 2))
625 allowed_position.push (straddle);
626 if ((multiple_i_ <= 1) && (beamdy_f <= staffline_f / 2))
627 allowed_position.push (sit);
628 if (beamdy_f >= -staffline_f / 2)
629 allowed_position.push (hang);
633 Interval iv = quantise_iv (allowed_position, space, dy_f);
635 Real quanty_f = dy_f - iv[SMALLER] <= iv[BIGGER] - dy_f ? iv[SMALLER] : iv[BIGGER];
637 quanty_f = iv[BIGGER];
639 // dim(left_y_) = internote
640 left_y_ = get_direction () * quanty_f / internote_f;
644 Beam::set_stemlens ()
646 Real staffline_f = paper_l ()->get_var ("stafflinethickness");
648 Real epsilon_f = staffline_f / 8;
651 // je bent zelf eng --hwn.
652 Real dy_f = check_stemlengths_f (false);
653 for (int i = 0; i < 2; i++) // 2 ?
655 left_y_ += dy_f * get_direction ();
656 quantise_left_y (dy_f);
657 dy_f = check_stemlengths_f (true);
658 if (abs (dy_f) <= epsilon_f)
666 Beam::set_beaming (Beaming_info_list *beaming)
669 for (int i=0; i < stem_count (); i++)
673 if (stem (i)->beams_i_drul_[d] < 0)
674 stem (i)->beams_i_drul_[d] = beaming->infos_.elem (i).beams_i_drul_[d];
676 while (flip (&d) != LEFT);
682 Beam::do_add_processing ()
684 for (int i=0; i < stem_count () ; i++)
688 multiple_i_ = multiple_i_ >? stem (i)->beams_i_drul_[d];
689 } while ((flip (&d)) != LEFT);
697 stem (0)->beams_i_drul_[LEFT] =0;
698 stem (stem_count () -1)->beams_i_drul_[RIGHT] =0;
705 beams to go with one stem.
710 Beam::stem_beams (Stem *here, Stem *next, Stem *prev) const
712 if ((next && !(next->hpos_f () > here->hpos_f ())) ||
713 (prev && !(prev->hpos_f () < here->hpos_f ())))
714 programming_error ("Beams are not left-to-right");
716 Real staffline_f = paper_l ()->get_var ("stafflinethickness");
717 Real interbeam_f = paper_l ()->interbeam_f (multiple_i_);
719 Real internote_f = here->staff_line_leading_f ()/2;
720 Real beam_f = gh_scm2double (get_elt_property ("beam-thickness"));;
722 Real dy = interbeam_f;
723 Real stemdx = staffline_f;
724 Real sl = slope_f_* internote_f;
731 if (!here->first_head ())
733 else if (here->type_i ()== 1)
734 nw_f = paper_l ()->get_var ("wholewidth");
735 else if (here->type_i () == 2)
736 nw_f = paper_l ()->get_var ("notewidth") * 0.8;
738 nw_f = paper_l ()->get_var ("quartwidth");
740 /* half beams extending to the left. */
743 int lhalfs= lhalfs = here->beams_i_drul_[LEFT] - prev->beams_i_drul_[RIGHT] ;
744 int lwholebeams= here->beams_i_drul_[LEFT] <? prev->beams_i_drul_[RIGHT] ;
746 Half beam should be one note-width,
747 but let's make sure two half-beams never touch
749 Real w = here->hpos_f () - prev->hpos_f ();
752 if (lhalfs) // generates warnings if not
753 a = lookup_l ()->beam (sl, w, beam_f);
754 a.translate (Offset (-w, -w * sl));
755 for (int j = 0; j < lhalfs; j++)
758 b.translate_axis (-get_direction () * dy * (lwholebeams+j), Y_AXIS);
759 leftbeams.add_molecule (b);
765 int rhalfs = here->beams_i_drul_[RIGHT] - next->beams_i_drul_[LEFT];
766 int rwholebeams = here->beams_i_drul_[RIGHT] <? next->beams_i_drul_[LEFT];
768 Real w = next->hpos_f () - here->hpos_f ();
769 Molecule a = lookup_l ()->beam (sl, w + stemdx, beam_f);
770 a.translate_axis( - stemdx/2, X_AXIS);
774 SCM gap = get_elt_property ("beam-gap");
775 if (gap != SCM_UNDEFINED)
777 int gap_i = gh_scm2int ( (gap));
778 int nogap = rwholebeams - gap_i;
780 for (; j < nogap; j++)
783 b.translate_axis (-get_direction () * dy * j, Y_AXIS);
784 rightbeams.add_molecule (b);
786 // TODO: notehead widths differ for different types
789 a = lookup_l ()->beam (sl, w + stemdx, beam_f);
792 for (; j < rwholebeams; j++)
795 if (!here->invisible_b ())
796 b.translate (Offset (gap_f, -get_direction () * dy * j));
798 b.translate (Offset (0, -get_direction () * dy * j));
799 rightbeams.add_molecule (b);
804 a = lookup_l ()->beam (sl, w, beam_f);
806 for (; j < rwholebeams + rhalfs; j++)
809 b.translate_axis (-get_direction () * dy * j, Y_AXIS);
810 rightbeams.add_molecule (b);
814 leftbeams.add_molecule (rightbeams);
817 Does beam quanting think of the asymetry of beams?
818 Refpoint is on bottom of symbol. (FIXTHAT) --hwn.