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 "cross-staff.hh"
37 Group_interface g (this, "stems");
46 TODO: Fix this class. This is wildly inefficient.
49 Beam::stem (int i)const
51 return Group_interface__extract_elements ((Beam*) this, (Stem*) 0, "stems")[i];
55 Beam::stem_count ()const
57 Group_interface gi (this, "stems");
62 Beam::stem_top ()const
64 return Group_interface__extract_elements ((Beam*) this, (Stem*) 0, "stems")[stem_count () - 1];
69 Beam::visible_stem_count () const
72 for (int i = 0; i < stem_count (); i++)
74 if (!stem (i)->invisible_b ())
81 Beam::first_visible_stem () const
83 for (int i = 0; i < stem_count (); i++)
86 if (!s->invisible_b ())
96 Beam::last_visible_stem () const
98 for (int i = stem_count (); i > 0; i--)
100 Stem* s = stem (i - 1);
101 if (!s->invisible_b ())
111 Beam::add_stem (Stem*s)
113 Group_interface gi (this, "stems");
116 s->add_dependency (this);
118 assert (!s->beam_l ());
119 s->set_elt_property ("beam", self_scm_);
121 if (!spanned_drul_[LEFT])
124 set_bounds (RIGHT,s);
128 Beam::do_brew_molecule_p () const
130 Molecule *mol_p = new Molecule;
134 Real x0 = first_visible_stem ()->hpos_f ();
135 for (int j=0; j <stem_count (); j++)
138 Stem * prev = (j > 0)? stem (j-1) : 0;
139 Stem * next = (j < stem_count ()-1) ? stem (j+1) :0;
141 Molecule sb = stem_beams (i, next, prev);
142 Real x = i->hpos_f ()-x0;
143 sb.translate (Offset (x, x * slope_f_ + left_y_));
144 mol_p->add_molecule (sb);
146 mol_p->translate_axis (x0
147 - spanned_drul_[LEFT]->relative_coordinate (0, X_AXIS), X_AXIS);
153 Beam::center () const
155 Real w = (first_visible_stem ()->note_delta_f () + extent (X_AXIS).length ())/2.0;
156 return Offset (w, w * slope_f_);
160 Simplistic auto-knees; only consider vertical gap between two
164 Beam::auto_knee (SCM gap, bool interstaff_b)
168 if (gap != SCM_UNDEFINED)
170 int auto_gap_i = gh_scm2int (gap);
171 for (int i=1; i < stem_count (); i++)
173 bool is_b = (bool)(calc_interstaff_dist (stem (i), this)
174 - calc_interstaff_dist (stem (i-1), this));
175 int l_y = (int)(stem (i-1)->chord_start_f ())
176 + (int)calc_interstaff_dist (stem (i-1), this);
177 int r_y = (int)(stem (i)->chord_start_f ())
178 + (int)calc_interstaff_dist (stem (i), this);
179 int gap_i = r_y - l_y;
182 Forced stem directions are ignored. If you don't want auto-knees,
183 don't set, or unset autoKneeGap/autoInterstaffKneeGap.
185 if ((abs (gap_i) >= auto_gap_i) && (!interstaff_b || is_b))
187 knee_y = (r_y + l_y) / 2;
195 for (int i=0; i < stem_count (); i++)
197 int y = (int)(stem (i)->chord_start_f ())
198 + (int)calc_interstaff_dist (stem (i), this);
199 stem (i)->set_direction ( y < knee_y ? UP : DOWN);
200 stem (i)->set_elt_property ("dir-forced", SCM_BOOL_T);
209 if (auto_knee (get_elt_property ("auto-interstaff-knee-gap"), true))
212 return auto_knee (get_elt_property ("auto-knee-gap"), false);
217 Beam::do_pre_processing ()
220 urg: it seems that info on whether beam (voice) dir was forced
221 is being junked here?
223 if (!get_direction ())
224 set_direction ( get_default_dir ());
226 set_direction (get_direction ());
230 Beam::do_print () const
233 DEBUG_OUT << "slope_f_ " << slope_f_ << "left ypos " << left_y_;
234 Spanner::do_print ();
239 Beam::do_post_processing ()
241 if (visible_stem_count () < 2)
243 warning (_ ("beam with less than two stems"));
244 set_elt_property ("transparent", SCM_BOOL_T);
251 if auto-knee did its work, most probably stem directions
252 have changed, so we must recalculate all.
254 set_direction (get_default_dir ());
255 set_direction (get_direction ());
257 /* auto-knees used to only work for slope = 0
258 anyway, should be able to set slope per beam
259 set_elt_property ("damping", gh_int2scm(1000));
271 Beam::do_width () const
273 return Interval (stem (0)->hpos_f (),
274 stems_.top ()->hpos_f ());
279 Beam::get_default_dir () const
281 Drul_array<int> total;
282 total[UP] = total[DOWN] = 0;
283 Drul_array<int> count;
284 count[UP] = count[DOWN] = 0;
287 for (int i=0; i <stem_count (); i++)
290 int current = s->get_direction ()
291 ? (1 + d * s->get_direction ())/2
292 : s->get_center_distance ((Direction)-d);
300 } while (flip(&d) != DOWN);
303 [Ross] states that the majority of the notes dictates the
304 direction (and not the mean of "center distance")
306 But is that because it really looks better, or because he wants
307 to provide some real simple hands-on rules?
309 We have our doubts, so we simply provide all sensible alternatives.
311 If dir is not determined: up (see stem::get_default_dir ()) */
313 Direction beam_dir = CENTER;
314 Direction neutral_dir = (Direction)(int)paper_l ()->get_var ("stem_default_neutral_direction");
316 SCM a = get_elt_property ("beam-dir-algorithm");
318 if (a == ly_symbol2scm ("majority")) // should get default from paper.
319 beam_dir = (count[UP] == count[DOWN]) ? neutral_dir
320 : (count[UP] > count[DOWN]) ? UP : DOWN;
321 else if (a == ly_symbol2scm ("mean"))
322 // mean center distance
323 beam_dir = (total[UP] == total[DOWN]) ? neutral_dir
324 : (total[UP] > total[DOWN]) ? UP : DOWN;
325 else if (a == ly_symbol2scm ("median"))
327 // median center distance
328 if (count[DOWN] && count[UP])
330 beam_dir = (total[UP] / count[UP] == total[DOWN] / count[DOWN])
332 : (total[UP] / count[UP] > total[DOWN] / count[DOWN]) ? UP : DOWN;
336 beam_dir = (count[UP] == count[DOWN]) ? neutral_dir
337 : (count[UP] > count[DOWN]) ? UP : DOWN;
345 Beam::set_direction (Direction d)
347 Directional_spanner::set_direction (d);
348 for (int i=0; i <stem_count (); i++)
351 s->set_elt_property ("beam-dir", gh_int2scm (d));
353 SCM force = s->remove_elt_property ("dir-forced");
354 if (force == SCM_UNDEFINED)
355 s->set_direction ( d);
360 See Documentation/tex/fonts.doc
366 assert (visible_stem_count () > 1);
369 Real x0 = first_visible_stem ()->hpos_f ();
370 for (int i=0; i < stem_count (); i++)
373 if (s->invisible_b ())
375 l.input.push (Offset (s->hpos_f () - x0, s->get_info ().idealy_f_));
377 l.minimise (slope_f_, left_y_);
381 ugh. Naming: this doesn't check, but sets as well.
384 Beam::check_stemlengths_f (bool set_b)
386 Real interbeam_f = paper_l ()->interbeam_f (multiplicity_i_);
388 Real beam_f = gh_scm2double (get_elt_property ("beam-thickness"));
389 Real staffline_f = paper_l ()-> get_var ("stafflinethickness");
390 Real epsilon_f = staffline_f / 8;
392 Real x0 = first_visible_stem ()->hpos_f ();
393 Real internote_f = paper_l ()->get_var ("interline");
394 for (int i=0; i < stem_count (); i++)
397 if (s->invisible_b ())
399 Real y = (s->hpos_f () - x0) * slope_f_ + left_y_;
400 Stem_info info = s->get_info ();
403 if (get_direction () != s->get_direction ())
405 y -= get_direction () * (beam_f / 2
406 + (multiplicity_i_ - 1) * interbeam_f);
408 && s->staff_symbol_l () != stem_top ()->staff_symbol_l ())
409 y += get_direction () * (multiplicity_i_ - (s->flag_i_ - 2) >? 0)
413 /* caution: stem measures in staff-positions */
415 s->set_stemend ((y - calc_interstaff_dist (s, this))
418 y *= get_direction ();
419 if (y > info.maxy_f_)
420 dy_f = dy_f <? info.maxy_f_ - y;
421 if (y < info.miny_f_)
423 // when all too short, normal stems win..
424 if (dy_f < -epsilon_f)
425 warning (_ ("weird beam vertical offset"));
426 dy_f = dy_f >? info.miny_f_ - y;
433 Beam::set_stem_shorten ()
438 assert (multiplicity_i_);
440 int total_count_i = 0;
441 int forced_count_i = 0;
442 for (int i=0; i < stem_count (); i++)
446 s->set_default_extents ();
447 if (s->invisible_b ())
449 if (((int)s->chord_start_f ()) && (s->get_direction () != s->get_default_dir ()))
454 Real internote_f = paper_l ()->get_var ("interline");
455 bool grace_b = get_elt_property ("grace") == SCM_BOOL_T;
456 String type_str = grace_b ? "grace_" : "";
457 int stem_max = (int)rint(paper_l ()->get_var ("stem_max"));
458 Real shorten_f = paper_l ()->get_var (type_str + "forced_stem_shorten"
459 + to_str (multiplicity_i_ <? stem_max)) * internote_f;
461 for (int i=0; i < stem_count (); i++)
465 Chord tremolo needs to beam over invisible stems of wholes
467 SCM trem = get_elt_property ("chord-tremolo");
468 if (gh_boolean_p (trem) && gh_scm2bool (trem))
470 if (s->invisible_b ())
474 if (s->get_direction () == get_direction ())
476 if (forced_count_i == total_count_i)
477 s->set_real ("shorten", shorten_f);
478 else if (forced_count_i > total_count_i / 2)
479 s->set_real ("shorten", shorten_f/2);
485 Beam::calculate_slope ()
488 slope_f_ = left_y_ = 0;
489 else if (first_visible_stem ()->get_info ().idealy_f_ == last_visible_stem ()->get_info ().idealy_f_)
492 left_y_ = first_visible_stem ()->get_info ().idealy_f_;
493 left_y_ *= get_direction ();
498 Real solved_slope_f = slope_f_;
501 steep slope running against lengthened stem is suspect
503 Real dx_f = stem (stem_count () -1)->hpos_f () - first_visible_stem ()->hpos_f ();
505 Real lengthened = paper_l ()->get_var ("beam_lengthened");
506 Real steep = paper_l ()->get_var ("beam_steep_slope");
507 if (((left_y_ - first_visible_stem ()->get_info ().idealy_f_ > lengthened)
508 && (slope_f_ > steep))
509 || ((left_y_ + slope_f_ * dx_f - last_visible_stem ()->get_info ().idealy_f_ > lengthened)
510 && (slope_f_ < -steep)))
516 This neat trick is by Werner Lemberg,
517 damped = tanh (slope_f_)
518 corresponds with some tables in [Wanske]
520 SCM damp = remove_elt_property ("damping");
521 int damping = 1; // ugh.
522 if (damp!= SCM_UNDEFINED)
523 damping = gh_int2scm (damp);
526 slope_f_ = 0.6 * tanh (slope_f_) / damping;
530 Real damped_slope_dy_f = (solved_slope_f - slope_f_) * dx_f / 2;
531 left_y_ += damped_slope_dy_f;
533 left_y_ *= get_direction ();
534 slope_f_ *= get_direction ();
542 [Ross] (simplification of)
543 Try to set slope_f_ complying with y-span of:
545 - beam_f / 2 + staffline_f / 2
546 - beam_f + staffline_f
550 SCM q = get_elt_property ("slope-quantisation");
552 if (q == ly_symbol2scm ("none"))
555 Real interline_f = stem (0)->staff_line_leading_f ();
556 Real staffline_f = paper_l ()->get_var ("stafflinethickness");
557 Real beam_f = gh_scm2double (get_elt_property ("beam-thickness"));;
559 Real dx_f = stem (stem_count () -1 )->hpos_f () - first_visible_stem ()->hpos_f ();
561 Real dy_f = dx_f * abs (slope_f_);
565 Array<Real> allowed_fraction (3);
566 allowed_fraction[0] = 0;
567 allowed_fraction[1] = (beam_f / 2 + staffline_f / 2);
568 allowed_fraction[2] = (beam_f + staffline_f);
570 Interval iv = quantise_iv (allowed_fraction, interline_f, dy_f);
571 quanty_f = (dy_f - iv[SMALLER] <= iv[BIGGER] - dy_f)
575 slope_f_ = (quanty_f / dx_f) * sign (slope_f_);
580 Prevent interference from stafflines and beams. See Documentation/tex/fonts.doc
584 Beam::quantise_left_y (bool extend_b)
587 we only need to quantise the start of the beam as dy is quantised too
588 if extend_b then stems must *not* get shorter
590 SCM q = get_elt_property ("slope-quantisation");
594 ----------------------------------------------------------
598 --------------########------------------------------------
601 hang straddle sit inter hang
604 Real space = stem (0)->staff_line_leading_f ();
605 Real staffline_f = paper_l ()->get_var ("stafflinethickness");
606 Real beam_f = gh_scm2double (get_elt_property ("beam-thickness"));;
610 it would be nice to have all allowed positions in a runtime matrix:
611 (multiplicity, minimum_beam_dy, maximum_beam_dy)
615 Real sit = beam_f / 2 - staffline_f / 2;
616 Real hang = space - beam_f / 2 + staffline_f / 2;
619 Put all allowed positions into an array.
620 Whether a position is allowed or not depends on
621 strictness of quantisation, multiplicity and direction.
623 For simplicity, we'll assume dir = UP and correct if
624 dir = DOWN afterwards.
626 // isn't this asymmetric ? --hwn
628 Real dy_f = get_direction () * left_y_;
630 Real beamdx_f = stem (stem_count () -1)->hpos_f () - first_visible_stem ()->hpos_f ();
631 Real beamdy_f = beamdx_f * slope_f_;
633 Array<Real> allowed_position;
634 if (q == ly_symbol2scm ("normal"))
636 if ((multiplicity_i_ <= 2) || (abs (beamdy_f) >= staffline_f / 2))
637 allowed_position.push (straddle);
638 if ((multiplicity_i_ <= 1) || (abs (beamdy_f) >= staffline_f / 2))
639 allowed_position.push (sit);
640 allowed_position.push (hang);
642 else if (q == ly_symbol2scm ("traditional"))
644 // TODO: check and fix TRADITIONAL
645 if ((multiplicity_i_ <= 2) || (abs (beamdy_f) >= staffline_f / 2))
646 allowed_position.push (straddle);
647 if ((multiplicity_i_ <= 1) && (beamdy_f <= staffline_f / 2))
648 allowed_position.push (sit);
649 if (beamdy_f >= -staffline_f / 2)
650 allowed_position.push (hang);
654 Interval iv = quantise_iv (allowed_position, space, dy_f);
656 Real quanty_f = dy_f - iv[SMALLER] <= iv[BIGGER] - dy_f ? iv[SMALLER] : iv[BIGGER];
658 quanty_f = iv[BIGGER];
660 left_y_ = get_direction () * quanty_f;
664 Beam::set_stemlens ()
666 Real staffline_f = paper_l ()->get_var ("stafflinethickness");
668 Real epsilon_f = staffline_f / 8;
671 // je bent zelf eng --hwn.
672 Real dy_f = check_stemlengths_f (false);
673 for (int i = 0; i < 2; i++) // 2 ?
675 left_y_ += dy_f * get_direction ();
676 quantise_left_y (dy_f);
677 dy_f = check_stemlengths_f (true);
678 if (abs (dy_f) <= epsilon_f)
686 Beam::set_beaming (Beaming_info_list *beaming)
689 for (int i=0; i < stem_count (); i++)
693 if (stem (i)->beams_i_drul_[d] < 0)
694 stem (i)->beams_i_drul_[d] = beaming->infos_.elem (i).beams_i_drul_[d];
696 while (flip (&d) != LEFT);
702 Beam::do_add_processing ()
704 for (int i=0; i < stem_count () ; i++)
708 multiplicity_i_ = multiplicity_i_ >? stem (i)->beams_i_drul_[d];
709 } while ((flip (&d)) != LEFT);
718 stem (0)->beams_i_drul_[LEFT] =0;
719 stem (stem_count () -1)->beams_i_drul_[RIGHT] =0;
727 beams to go with one stem.
732 Beam::stem_beams (Stem *here, Stem *next, Stem *prev) const
734 if ((next && !(next->hpos_f () > here->hpos_f ())) ||
735 (prev && !(prev->hpos_f () < here->hpos_f ())))
736 programming_error ("Beams are not left-to-right");
738 Real staffline_f = paper_l ()->get_var ("stafflinethickness");
739 Real interbeam_f = paper_l ()->interbeam_f (multiplicity_i_);
740 Real beam_f = gh_scm2double (get_elt_property ("beam-thickness"));;
742 Real dy = interbeam_f;
743 Real stemdx = staffline_f;
751 if (!here->first_head ())
753 else if (here->type_i ()== 1)
754 nw_f = paper_l ()->get_var ("wholewidth");
755 else if (here->type_i () == 2)
756 nw_f = paper_l ()->get_var ("notewidth") * 0.8;
758 nw_f = paper_l ()->get_var ("quartwidth");
760 /* half beams extending to the left. */
763 int lhalfs= lhalfs = here->beams_i_drul_[LEFT] - prev->beams_i_drul_[RIGHT] ;
764 int lwholebeams= here->beams_i_drul_[LEFT] <? prev->beams_i_drul_[RIGHT] ;
766 Half beam should be one note-width,
767 but let's make sure two half-beams never touch
769 Real w = here->hpos_f () - prev->hpos_f ();
772 if (lhalfs) // generates warnings if not
773 a = lookup_l ()->beam (sl, w, beam_f);
774 a.translate (Offset (-w, -w * sl));
775 for (int j = 0; j < lhalfs; j++)
778 b.translate_axis (-get_direction () * dy * (lwholebeams+j), Y_AXIS);
779 leftbeams.add_molecule (b);
785 int rhalfs = here->beams_i_drul_[RIGHT] - next->beams_i_drul_[LEFT];
786 int rwholebeams = here->beams_i_drul_[RIGHT] <? next->beams_i_drul_[LEFT];
788 Real w = next->hpos_f () - here->hpos_f ();
789 Molecule a = lookup_l ()->beam (sl, w + stemdx, beam_f);
790 a.translate_axis( - stemdx/2, X_AXIS);
794 SCM gap = get_elt_property ("beam-gap");
795 if (gap != SCM_UNDEFINED)
797 int gap_i = gh_scm2int ( (gap));
798 int nogap = rwholebeams - gap_i;
800 for (; j < nogap; j++)
803 b.translate_axis (-get_direction () * dy * j, Y_AXIS);
804 rightbeams.add_molecule (b);
806 // TODO: notehead widths differ for different types
809 a = lookup_l ()->beam (sl, w + stemdx, beam_f);
812 for (; j < rwholebeams; j++)
815 if (!here->invisible_b ())
816 b.translate (Offset (gap_f, -get_direction () * dy * j));
818 b.translate (Offset (0, -get_direction () * dy * j));
819 rightbeams.add_molecule (b);
824 a = lookup_l ()->beam (sl, w, beam_f);
826 for (; j < rwholebeams + rhalfs; j++)
829 b.translate_axis (-get_direction () * dy * j, Y_AXIS);
830 rightbeams.add_molecule (b);
834 leftbeams.add_molecule (rightbeams);
837 Does beam quanting think of the asymetry of beams?
838 Refpoint is on bottom of symbol. (FIXTHAT) --hwn.