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>
23 #include "dimensions.hh"
27 #include "molecule.hh"
28 #include "leastsquares.hh"
30 #include "paper-def.hh"
32 #include "group-interface.hh"
33 #include "staff-symbol-referencer.hh"
34 #include "cross-staff.hh"
38 Group_interface g (this, "stems");
47 TODO: Fix this class. This is wildly inefficient.
50 Beam::stem (int i)const
52 return Group_interface__extract_elements ((Beam*) this, (Stem*) 0, "stems")[i];
56 Beam::stem_count ()const
58 Group_interface gi (this, "stems");
63 Beam::stem_top ()const
65 return Group_interface__extract_elements ((Beam*) this, (Stem*) 0, "stems")[stem_count () - 1];
70 Beam::visible_stem_count () const
73 for (int i = 0; i < stem_count (); i++)
75 if (!stem (i)->invisible_b ())
82 Beam::first_visible_stem () const
84 for (int i = 0; i < stem_count (); i++)
87 if (!s->invisible_b ())
97 Beam::last_visible_stem () const
99 for (int i = stem_count (); i > 0; i--)
101 Stem* s = stem (i - 1);
102 if (!s->invisible_b ())
112 Beam::add_stem (Stem*s)
114 Group_interface gi (this, "stems");
117 s->add_dependency (this);
119 assert (!s->beam_l ());
120 s->set_elt_property ("beam", self_scm_);
122 if (!spanned_drul_[LEFT])
125 set_bounds (RIGHT,s);
129 Beam::do_brew_molecule_p () const
131 Molecule *mol_p = new Molecule;
135 Real x0 = first_visible_stem ()->hpos_f ();
136 for (int j=0; j <stem_count (); j++)
139 Stem * prev = (j > 0)? stem (j-1) : 0;
140 Stem * next = (j < stem_count ()-1) ? stem (j+1) :0;
142 Molecule sb = stem_beams (i, next, prev);
143 Real x = i->hpos_f ()-x0;
144 sb.translate (Offset (x, x * slope_f_ + left_y_));
145 mol_p->add_molecule (sb);
147 mol_p->translate_axis (x0
148 - spanned_drul_[LEFT]->relative_coordinate (0, X_AXIS), X_AXIS);
154 Beam::center () const
156 Real w = (first_visible_stem ()->note_delta_f () + extent (X_AXIS).length ())/2.0;
157 return Offset (w, w * slope_f_);
161 Simplistic auto-knees; only consider vertical gap between two
165 Beam::auto_knee (SCM gap, bool interstaff_b)
169 if (gap != SCM_UNDEFINED)
171 int auto_gap_i = gh_scm2int (gap);
172 for (int i=1; i < stem_count (); i++)
174 bool is_b = (bool)(calc_interstaff_dist (stem (i), this)
175 - calc_interstaff_dist (stem (i-1), this));
176 int l_y = (int)(stem (i-1)->chord_start_f ())
177 + (int)calc_interstaff_dist (stem (i-1), this);
178 int r_y = (int)(stem (i)->chord_start_f ())
179 + (int)calc_interstaff_dist (stem (i), this);
180 int gap_i = r_y - l_y;
183 Forced stem directions are ignored. If you don't want auto-knees,
184 don't set, or unset autoKneeGap/autoInterstaffKneeGap.
186 if ((abs (gap_i) >= auto_gap_i) && (!interstaff_b || is_b))
188 knee_y = (r_y + l_y) / 2;
196 for (int i=0; i < stem_count (); i++)
198 int y = (int)(stem (i)->chord_start_f ())
199 + (int)calc_interstaff_dist (stem (i), this);
200 stem (i)->set_direction ( y < knee_y ? UP : DOWN);
201 stem (i)->set_elt_property ("dir-forced", SCM_BOOL_T);
210 if (auto_knee (get_elt_property ("auto-interstaff-knee-gap"), true))
213 return auto_knee (get_elt_property ("auto-knee-gap"), false);
218 Beam::do_pre_processing ()
221 urg: it seems that info on whether beam (voice) dir was forced
222 is being junked here?
224 if (!get_direction ())
225 set_direction ( get_default_dir ());
227 set_direction (get_direction ());
231 Beam::do_print () const
234 DEBUG_OUT << "slope_f_ " << slope_f_ << "left ypos " << left_y_;
235 Spanner::do_print ();
240 Beam::do_post_processing ()
242 if (visible_stem_count () < 2)
244 warning (_ ("beam with less than two stems"));
245 set_elt_property ("transparent", SCM_BOOL_T);
252 if auto-knee did its work, most probably stem directions
253 have changed, so we must recalculate all.
255 set_direction (get_default_dir ());
256 set_direction (get_direction ());
258 /* auto-knees used to only work for slope = 0
259 anyway, should be able to set slope per beam
260 set_elt_property ("damping", gh_int2scm(1000));
273 Beam::get_default_dir () const
275 Drul_array<int> total;
276 total[UP] = total[DOWN] = 0;
277 Drul_array<int> count;
278 count[UP] = count[DOWN] = 0;
281 for (int i=0; i <stem_count (); i++)
284 int current = s->get_direction ()
285 ? (1 + d * s->get_direction ())/2
286 : s->get_center_distance ((Direction)-d);
294 } while (flip(&d) != DOWN);
297 [Ross] states that the majority of the notes dictates the
298 direction (and not the mean of "center distance")
300 But is that because it really looks better, or because he wants
301 to provide some real simple hands-on rules?
303 We have our doubts, so we simply provide all sensible alternatives.
305 If dir is not determined: up (see stem::get_default_dir ()) */
307 Direction beam_dir = CENTER;
308 Direction neutral_dir = (Direction)(int)paper_l ()->get_var ("stem_default_neutral_direction");
310 SCM a = get_elt_property ("beam-dir-algorithm");
312 if (a == ly_symbol2scm ("majority")) // should get default from paper.
313 beam_dir = (count[UP] == count[DOWN]) ? neutral_dir
314 : (count[UP] > count[DOWN]) ? UP : DOWN;
315 else if (a == ly_symbol2scm ("mean"))
316 // mean center distance
317 beam_dir = (total[UP] == total[DOWN]) ? neutral_dir
318 : (total[UP] > total[DOWN]) ? UP : DOWN;
319 else if (a == ly_symbol2scm ("median"))
321 // median center distance
322 if (count[DOWN] && count[UP])
324 beam_dir = (total[UP] / count[UP] == total[DOWN] / count[DOWN])
326 : (total[UP] / count[UP] > total[DOWN] / count[DOWN]) ? UP : DOWN;
330 beam_dir = (count[UP] == count[DOWN]) ? neutral_dir
331 : (count[UP] > count[DOWN]) ? UP : DOWN;
339 Beam::set_direction (Direction d)
341 Directional_spanner::set_direction (d);
342 for (int i=0; i <stem_count (); i++)
345 s->set_elt_property ("beam-dir", gh_int2scm (d));
347 SCM force = s->get_elt_property ("dir-forced"); // remove_prop?
348 if (force == SCM_UNDEFINED)
349 s->set_direction ( d);
354 See Documentation/tex/fonts.doc
360 assert (visible_stem_count () > 1);
363 Real x0 = first_visible_stem ()->hpos_f ();
364 for (int i=0; i < stem_count (); i++)
367 if (s->invisible_b ())
369 l.input.push (Offset (s->hpos_f () - x0, s->calc_stem_info ().idealy_f_));
371 l.minimise (slope_f_, left_y_);
375 ugh. Naming: this doesn't check, but sets as well.
378 Beam::check_stemlengths_f (bool set_b)
380 Real interbeam_f = paper_l ()->interbeam_f (multiplicity_i_);
382 Real beam_f = gh_scm2double (get_elt_property ("beam-thickness"));
383 Real staffline_f = paper_l ()-> get_var ("stafflinethickness");
384 Real epsilon_f = staffline_f / 8;
386 Real x0 = first_visible_stem ()->hpos_f ();
387 Real internote_f = paper_l ()->get_var ("interline");
388 for (int i=0; i < stem_count (); i++)
391 if (s->invisible_b ())
393 Real y = (s->hpos_f () - x0) * slope_f_ + left_y_;
394 Stem_info info = s->calc_stem_info ();
397 if (get_direction () != s->get_direction ())
399 y -= get_direction () * (beam_f / 2
400 + (multiplicity_i_ - 1) * interbeam_f);
403 Staff_symbol_referencer_interface s1 (s);
404 Staff_symbol_referencer_interface s2 (stem_top ());
407 && s1.staff_symbol_l () != s2.staff_symbol_l ())
408 y += get_direction () * (multiplicity_i_ - (s->flag_i () - 2) >? 0)
412 /* caution: stem measures in staff-positions */
414 s->set_stemend ((y - calc_interstaff_dist (s, this))
417 y *= get_direction ();
418 if (y > info.maxy_f_)
419 dy_f = dy_f <? info.maxy_f_ - y;
420 if (y < info.miny_f_)
422 // when all too short, normal stems win..
423 if (dy_f < -epsilon_f)
424 warning (_ ("weird beam vertical offset"));
425 dy_f = dy_f >? info.miny_f_ - y;
432 Beam::set_stem_shorten ()
437 assert (multiplicity_i_);
439 int total_count_i = 0;
440 int forced_count_i = 0;
441 for (int i=0; i < stem_count (); i++)
445 s->set_default_extents ();
446 if (s->invisible_b ())
448 if (((int)s->chord_start_f ()) && (s->get_direction () != s->get_default_dir ()))
453 Real internote_f = paper_l ()->get_var ("interline");
454 bool grace_b = get_elt_property ("grace") == SCM_BOOL_T;
455 String type_str = grace_b ? "grace_" : "";
456 int stem_max = (int)rint(paper_l ()->get_var ("stem_max"));
457 Real shorten_f = paper_l ()->get_var (type_str + "forced_stem_shorten"
458 + to_str (multiplicity_i_ <? stem_max)) * internote_f;
460 for (int i=0; i < stem_count (); i++)
464 Chord tremolo needs to beam over invisible stems of wholes
466 SCM trem = get_elt_property ("chord-tremolo");
467 if (gh_boolean_p (trem) && gh_scm2bool (trem))
469 if (s->invisible_b ())
473 if (s->get_direction () == get_direction ())
475 if (forced_count_i == total_count_i)
476 s->set_real ("shorten", shorten_f);
477 else if (forced_count_i > total_count_i / 2)
478 s->set_real ("shorten", shorten_f/2);
484 Beam::calculate_slope ()
487 slope_f_ = left_y_ = 0;
488 else if (first_visible_stem ()->calc_stem_info ().idealy_f_ == last_visible_stem ()->calc_stem_info ().idealy_f_)
491 left_y_ = first_visible_stem ()->calc_stem_info ().idealy_f_;
492 left_y_ *= get_direction ();
497 Real solved_slope_f = slope_f_;
500 steep slope running against lengthened stem is suspect
502 Real dx_f = stem (stem_count () -1)->hpos_f () - first_visible_stem ()->hpos_f ();
504 Real lengthened = paper_l ()->get_var ("beam_lengthened");
505 Real steep = paper_l ()->get_var ("beam_steep_slope");
506 if (((left_y_ - first_visible_stem ()->calc_stem_info ().idealy_f_ > lengthened)
507 && (slope_f_ > steep))
508 || ((left_y_ + slope_f_ * dx_f - last_visible_stem ()->calc_stem_info ().idealy_f_ > lengthened)
509 && (slope_f_ < -steep)))
515 This neat trick is by Werner Lemberg,
516 damped = tanh (slope_f_)
517 corresponds with some tables in [Wanske]
519 SCM damp = remove_elt_property ("damping");
520 int damping = 1; // ugh.
521 if (damp!= SCM_UNDEFINED)
522 damping = gh_int2scm (damp);
525 slope_f_ = 0.6 * tanh (slope_f_) / damping;
529 Real damped_slope_dy_f = (solved_slope_f - slope_f_) * dx_f / 2;
530 left_y_ += damped_slope_dy_f;
532 left_y_ *= get_direction ();
533 slope_f_ *= get_direction ();
541 [Ross] (simplification of)
542 Try to set slope_f_ complying with y-span of:
544 - beam_f / 2 + staffline_f / 2
545 - beam_f + staffline_f
549 SCM q = get_elt_property ("slope-quantisation");
551 if (q == ly_symbol2scm ("none"))
554 Staff_symbol_referencer_interface st (this);
555 Real interline_f = st.staff_line_leading_f ();
557 Real staffline_f = paper_l ()->get_var ("stafflinethickness");
558 Real beam_f = gh_scm2double (get_elt_property ("beam-thickness"));;
560 Real dx_f = stem (stem_count () -1 )->hpos_f () - first_visible_stem ()->hpos_f ();
562 Real dy_f = dx_f * abs (slope_f_);
566 Array<Real> allowed_fraction (3);
567 allowed_fraction[0] = 0;
568 allowed_fraction[1] = (beam_f / 2 + staffline_f / 2);
569 allowed_fraction[2] = (beam_f + staffline_f);
571 Interval iv = quantise_iv (allowed_fraction, interline_f, dy_f);
572 quanty_f = (dy_f - iv[SMALLER] <= iv[BIGGER] - dy_f)
576 slope_f_ = (quanty_f / dx_f) * sign (slope_f_);
581 Prevent interference from stafflines and beams. See Documentation/tex/fonts.doc
585 Beam::quantise_left_y (bool extend_b)
588 we only need to quantise the start of the beam as dy is quantised too
589 if extend_b then stems must *not* get shorter
591 SCM q = get_elt_property ("slope-quantisation");
595 ----------------------------------------------------------
599 --------------########------------------------------------
602 hang straddle sit inter hang
605 Staff_symbol_referencer_interface sinf (this);
606 Real space = sinf.staff_line_leading_f ();
607 Real staffline_f = paper_l ()->get_var ("stafflinethickness");
608 Real beam_f = gh_scm2double (get_elt_property ("beam-thickness"));;
612 it would be nice to have all allowed positions in a runtime matrix:
613 (multiplicity, minimum_beam_dy, maximum_beam_dy)
617 Real sit = beam_f / 2 - staffline_f / 2;
618 Real hang = space - beam_f / 2 + staffline_f / 2;
621 Put all allowed positions into an array.
622 Whether a position is allowed or not depends on
623 strictness of quantisation, multiplicity and direction.
625 For simplicity, we'll assume dir = UP and correct if
626 dir = DOWN afterwards.
628 // isn't this asymmetric ? --hwn
630 Real dy_f = get_direction () * left_y_;
632 Real beamdx_f = stem (stem_count () -1)->hpos_f () - first_visible_stem ()->hpos_f ();
633 Real beamdy_f = beamdx_f * slope_f_;
635 Array<Real> allowed_position;
636 if (q == ly_symbol2scm ("normal"))
638 if ((multiplicity_i_ <= 2) || (abs (beamdy_f) >= staffline_f / 2))
639 allowed_position.push (straddle);
640 if ((multiplicity_i_ <= 1) || (abs (beamdy_f) >= staffline_f / 2))
641 allowed_position.push (sit);
642 allowed_position.push (hang);
644 else if (q == ly_symbol2scm ("traditional"))
646 // TODO: check and fix TRADITIONAL
647 if ((multiplicity_i_ <= 2) || (abs (beamdy_f) >= staffline_f / 2))
648 allowed_position.push (straddle);
649 if ((multiplicity_i_ <= 1) && (beamdy_f <= staffline_f / 2))
650 allowed_position.push (sit);
651 if (beamdy_f >= -staffline_f / 2)
652 allowed_position.push (hang);
656 Interval iv = quantise_iv (allowed_position, space, dy_f);
658 Real quanty_f = dy_f - iv[SMALLER] <= iv[BIGGER] - dy_f ? iv[SMALLER] : iv[BIGGER];
660 quanty_f = iv[BIGGER];
662 left_y_ = get_direction () * quanty_f;
666 Beam::set_stemlens ()
668 Real staffline_f = paper_l ()->get_var ("stafflinethickness");
670 Real epsilon_f = staffline_f / 8;
673 // je bent zelf eng --hwn.
674 Real dy_f = check_stemlengths_f (false);
675 for (int i = 0; i < 2; i++) // 2 ?
677 left_y_ += dy_f * get_direction ();
678 quantise_left_y (dy_f);
679 dy_f = check_stemlengths_f (true);
680 if (abs (dy_f) <= epsilon_f)
688 Beam::set_beaming (Beaming_info_list *beaming)
691 for (int i=0; i < stem_count (); i++)
695 if (stem (i)->beams_i_drul_[d] < 0)
696 stem (i)->beams_i_drul_[d] = beaming->infos_.elem (i).beams_i_drul_[d];
698 while (flip (&d) != LEFT);
704 Beam::do_add_processing ()
706 for (int i=0; i < stem_count () ; i++)
710 multiplicity_i_ = multiplicity_i_ >? stem (i)->beams_i_drul_[d];
711 } while ((flip (&d)) != LEFT);
719 beams to go with one stem.
724 Beam::stem_beams (Stem *here, Stem *next, Stem *prev) const
726 if ((next && !(next->hpos_f () > here->hpos_f ())) ||
727 (prev && !(prev->hpos_f () < here->hpos_f ())))
728 programming_error ("Beams are not left-to-right");
730 Real staffline_f = paper_l ()->get_var ("stafflinethickness");
731 Real interbeam_f = paper_l ()->interbeam_f (multiplicity_i_);
732 Real beam_f = gh_scm2double (get_elt_property ("beam-thickness"));;
734 Real dy = interbeam_f;
735 Real stemdx = staffline_f;
743 if (!here->first_head ())
745 else if (here->type_i ()== 1)
746 nw_f = paper_l ()->get_var ("wholewidth");
747 else if (here->type_i () == 2)
748 nw_f = paper_l ()->get_var ("notewidth") * 0.8;
750 nw_f = paper_l ()->get_var ("quartwidth");
752 /* half beams extending to the left. */
755 int lhalfs= lhalfs = here->beams_i_drul_[LEFT] - prev->beams_i_drul_[RIGHT] ;
756 int lwholebeams= here->beams_i_drul_[LEFT] <? prev->beams_i_drul_[RIGHT] ;
758 Half beam should be one note-width,
759 but let's make sure two half-beams never touch
761 Real w = here->hpos_f () - prev->hpos_f ();
764 if (lhalfs) // generates warnings if not
765 a = lookup_l ()->beam (sl, w, beam_f);
766 a.translate (Offset (-w, -w * sl));
767 for (int j = 0; j < lhalfs; j++)
770 b.translate_axis (-get_direction () * dy * (lwholebeams+j), Y_AXIS);
771 leftbeams.add_molecule (b);
777 int rhalfs = here->beams_i_drul_[RIGHT] - next->beams_i_drul_[LEFT];
778 int rwholebeams = here->beams_i_drul_[RIGHT] <? next->beams_i_drul_[LEFT];
780 Real w = next->hpos_f () - here->hpos_f ();
781 Molecule a = lookup_l ()->beam (sl, w + stemdx, beam_f);
782 a.translate_axis( - stemdx/2, X_AXIS);
786 SCM gap = get_elt_property ("beam-gap");
787 if (gap != SCM_UNDEFINED)
789 int gap_i = gh_scm2int ( (gap));
790 int nogap = rwholebeams - gap_i;
792 for (; j < nogap; j++)
795 b.translate_axis (-get_direction () * dy * j, Y_AXIS);
796 rightbeams.add_molecule (b);
798 // TODO: notehead widths differ for different types
801 a = lookup_l ()->beam (sl, w + stemdx, beam_f);
804 for (; j < rwholebeams; j++)
807 if (!here->invisible_b ())
808 b.translate (Offset (gap_f, -get_direction () * dy * j));
810 b.translate (Offset (0, -get_direction () * dy * j));
811 rightbeams.add_molecule (b);
816 a = lookup_l ()->beam (sl, w, beam_f);
818 for (; j < rwholebeams + rhalfs; j++)
821 b.translate_axis (-get_direction () * dy * j, Y_AXIS);
822 rightbeams.add_molecule (b);
826 leftbeams.add_molecule (rightbeams);
829 Does beam quanting think of the asymetry of beams?
830 Refpoint is on bottom of symbol. (FIXTHAT) --hwn.