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");
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));
272 Beam::get_default_dir () const
274 Drul_array<int> total;
275 total[UP] = total[DOWN] = 0;
276 Drul_array<int> count;
277 count[UP] = count[DOWN] = 0;
280 for (int i=0; i <stem_count (); i++)
283 int current = s->get_direction ()
284 ? (1 + d * s->get_direction ())/2
285 : s->get_center_distance ((Direction)-d);
293 } while (flip(&d) != DOWN);
296 [Ross] states that the majority of the notes dictates the
297 direction (and not the mean of "center distance")
299 But is that because it really looks better, or because he wants
300 to provide some real simple hands-on rules?
302 We have our doubts, so we simply provide all sensible alternatives.
304 If dir is not determined: up (see stem::get_default_dir ()) */
306 Direction beam_dir = CENTER;
307 Direction neutral_dir = (Direction)(int)paper_l ()->get_var ("stem_default_neutral_direction");
309 SCM a = get_elt_property ("beam-dir-algorithm");
311 if (a == ly_symbol2scm ("majority")) // should get default from paper.
312 beam_dir = (count[UP] == count[DOWN]) ? neutral_dir
313 : (count[UP] > count[DOWN]) ? UP : DOWN;
314 else if (a == ly_symbol2scm ("mean"))
315 // mean center distance
316 beam_dir = (total[UP] == total[DOWN]) ? neutral_dir
317 : (total[UP] > total[DOWN]) ? UP : DOWN;
318 else if (a == ly_symbol2scm ("median"))
320 // median center distance
321 if (count[DOWN] && count[UP])
323 beam_dir = (total[UP] / count[UP] == total[DOWN] / count[DOWN])
325 : (total[UP] / count[UP] > total[DOWN] / count[DOWN]) ? UP : DOWN;
329 beam_dir = (count[UP] == count[DOWN]) ? neutral_dir
330 : (count[UP] > count[DOWN]) ? UP : DOWN;
338 Beam::set_direction (Direction d)
340 Directional_spanner::set_direction (d);
341 for (int i=0; i <stem_count (); i++)
344 s->set_elt_property ("beam-dir", gh_int2scm (d));
346 SCM force = s->get_elt_property ("dir-forced"); // remove_prop?
347 if (force == SCM_UNDEFINED)
348 s->set_direction ( d);
353 See Documentation/tex/fonts.doc
359 assert (visible_stem_count () > 1);
362 Real x0 = first_visible_stem ()->hpos_f ();
363 for (int i=0; i < stem_count (); i++)
366 if (s->invisible_b ())
368 l.input.push (Offset (s->hpos_f () - x0, s->calc_stem_info ().idealy_f_));
370 l.minimise (slope_f_, left_y_);
374 ugh. Naming: this doesn't check, but sets as well.
377 Beam::check_stemlengths_f (bool set_b)
379 int multiplicity = multiplicity_i ();
381 Real interbeam_f = paper_l ()->interbeam_f (multiplicity);
383 Real beam_f = gh_scm2double (get_elt_property ("beam-thickness"));
384 Real staffline_f = paper_l ()-> get_var ("stafflinethickness");
385 Real epsilon_f = staffline_f / 8;
387 Real x0 = first_visible_stem ()->hpos_f ();
388 Real internote_f = paper_l ()->get_var ("interline");
389 for (int i=0; i < stem_count (); i++)
392 if (s->invisible_b ())
394 Real y = (s->hpos_f () - x0) * slope_f_ + left_y_;
395 Stem_info info = s->calc_stem_info ();
398 if (get_direction () != s->get_direction ())
400 y -= get_direction () * (beam_f / 2
401 + (multiplicity - 1) * interbeam_f);
404 Staff_symbol_referencer_interface s1 (s);
405 Staff_symbol_referencer_interface s2 (stem_top ());
408 && s1.staff_symbol_l () != s2.staff_symbol_l ())
409 y += get_direction () * (multiplicity - (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 int multiplicity = multiplicity_i();
440 if (multiplicity <= 0)
442 programming_error ("Singular beam");
446 int total_count_i = 0;
447 int forced_count_i = 0;
448 for (int i=0; i < stem_count (); i++)
452 s->set_default_extents ();
453 if (s->invisible_b ())
455 if (((int)s->chord_start_f ()) && (s->get_direction () != s->get_default_dir ()))
460 Real internote_f = paper_l ()->get_var ("interline");
461 bool grace_b = get_elt_property ("grace") == SCM_BOOL_T;
462 String type_str = grace_b ? "grace_" : "";
463 int stem_max = (int)rint(paper_l ()->get_var ("stem_max"));
464 Real shorten_f = paper_l ()->get_var (type_str + "forced_stem_shorten"
465 + to_str (multiplicity <? stem_max)) * internote_f;
467 for (int i=0; i < stem_count (); i++)
471 Chord tremolo needs to beam over invisible stems of wholes
473 SCM trem = get_elt_property ("chord-tremolo");
474 if (gh_boolean_p (trem) && gh_scm2bool (trem))
476 if (s->invisible_b ())
480 if (s->get_direction () == get_direction ())
482 if (forced_count_i == total_count_i)
483 s->set_real ("shorten", shorten_f);
484 else if (forced_count_i > total_count_i / 2)
485 s->set_real ("shorten", shorten_f/2);
491 Beam::calculate_slope ()
494 slope_f_ = left_y_ = 0;
495 else if (first_visible_stem ()->calc_stem_info ().idealy_f_ == last_visible_stem ()->calc_stem_info ().idealy_f_)
498 left_y_ = first_visible_stem ()->calc_stem_info ().idealy_f_;
499 left_y_ *= get_direction ();
504 Real solved_slope_f = slope_f_;
507 steep slope running against lengthened stem is suspect
509 Real dx_f = stem (stem_count () -1)->hpos_f () - first_visible_stem ()->hpos_f ();
511 Real lengthened = paper_l ()->get_var ("beam_lengthened");
512 Real steep = paper_l ()->get_var ("beam_steep_slope");
513 if (((left_y_ - first_visible_stem ()->calc_stem_info ().idealy_f_ > lengthened)
514 && (slope_f_ > steep))
515 || ((left_y_ + slope_f_ * dx_f - last_visible_stem ()->calc_stem_info ().idealy_f_ > lengthened)
516 && (slope_f_ < -steep)))
522 This neat trick is by Werner Lemberg,
523 damped = tanh (slope_f_)
524 corresponds with some tables in [Wanske]
526 SCM damp = remove_elt_property ("damping");
527 int damping = 1; // ugh.
528 if (damp!= SCM_UNDEFINED)
529 damping = gh_int2scm (damp);
532 slope_f_ = 0.6 * tanh (slope_f_) / damping;
536 Real damped_slope_dy_f = (solved_slope_f - slope_f_) * dx_f / 2;
537 left_y_ += damped_slope_dy_f;
539 left_y_ *= get_direction ();
540 slope_f_ *= get_direction ();
548 [Ross] (simplification of)
549 Try to set slope_f_ complying with y-span of:
551 - beam_f / 2 + staffline_f / 2
552 - beam_f + staffline_f
556 SCM q = get_elt_property ("slope-quantisation");
558 if (q == ly_symbol2scm ("none"))
561 Staff_symbol_referencer_interface st (this);
562 Real interline_f = st.staff_line_leading_f ();
564 Real staffline_f = paper_l ()->get_var ("stafflinethickness");
565 Real beam_f = gh_scm2double (get_elt_property ("beam-thickness"));;
567 Real dx_f = stem (stem_count () -1 )->hpos_f () - first_visible_stem ()->hpos_f ();
569 Real dy_f = dx_f * abs (slope_f_);
573 Array<Real> allowed_fraction (3);
574 allowed_fraction[0] = 0;
575 allowed_fraction[1] = (beam_f / 2 + staffline_f / 2);
576 allowed_fraction[2] = (beam_f + staffline_f);
578 Interval iv = quantise_iv (allowed_fraction, interline_f, dy_f);
579 quanty_f = (dy_f - iv[SMALLER] <= iv[BIGGER] - dy_f)
583 slope_f_ = (quanty_f / dx_f) * sign (slope_f_);
588 Prevent interference from stafflines and beams. See Documentation/tex/fonts.doc
592 Beam::quantise_left_y (bool extend_b)
595 we only need to quantise the start of the beam as dy is quantised too
596 if extend_b then stems must *not* get shorter
598 SCM q = get_elt_property ("slope-quantisation");
602 ----------------------------------------------------------
606 --------------########------------------------------------
609 hang straddle sit inter hang
612 Staff_symbol_referencer_interface sinf (this);
613 Real space = sinf.staff_line_leading_f ();
614 Real staffline_f = paper_l ()->get_var ("stafflinethickness");
615 Real beam_f = gh_scm2double (get_elt_property ("beam-thickness"));;
619 it would be nice to have all allowed positions in a runtime matrix:
620 (multiplicity, minimum_beam_dy, maximum_beam_dy)
624 Real sit = beam_f / 2 - staffline_f / 2;
625 Real hang = space - beam_f / 2 + staffline_f / 2;
628 Put all allowed positions into an array.
629 Whether a position is allowed or not depends on
630 strictness of quantisation, multiplicity and direction.
632 For simplicity, we'll assume dir = UP and correct if
633 dir = DOWN afterwards.
635 // isn't this asymmetric ? --hwn
637 Real dy_f = get_direction () * left_y_;
639 Real beamdx_f = stem (stem_count () -1)->hpos_f () - first_visible_stem ()->hpos_f ();
640 Real beamdy_f = beamdx_f * slope_f_;
642 int multiplicity = multiplicity_i ();
644 Array<Real> allowed_position;
645 if (q == ly_symbol2scm ("normal"))
647 if ((multiplicity <= 2) || (abs (beamdy_f) >= staffline_f / 2))
648 allowed_position.push (straddle);
649 if ((multiplicity <= 1) || (abs (beamdy_f) >= staffline_f / 2))
650 allowed_position.push (sit);
651 allowed_position.push (hang);
653 else if (q == ly_symbol2scm ("traditional"))
655 // TODO: check and fix TRADITIONAL
656 if ((multiplicity <= 2) || (abs (beamdy_f) >= staffline_f / 2))
657 allowed_position.push (straddle);
658 if ((multiplicity <= 1) && (beamdy_f <= staffline_f / 2))
659 allowed_position.push (sit);
660 if (beamdy_f >= -staffline_f / 2)
661 allowed_position.push (hang);
665 Interval iv = quantise_iv (allowed_position, space, dy_f);
667 Real quanty_f = dy_f - iv[SMALLER] <= iv[BIGGER] - dy_f ? iv[SMALLER] : iv[BIGGER];
669 quanty_f = iv[BIGGER];
671 left_y_ = get_direction () * quanty_f;
675 Beam::set_stemlens ()
677 Real staffline_f = paper_l ()->get_var ("stafflinethickness");
679 Real epsilon_f = staffline_f / 8;
682 // je bent zelf eng --hwn.
683 Real dy_f = check_stemlengths_f (false);
684 for (int i = 0; i < 2; i++) // 2 ?
686 left_y_ += dy_f * get_direction ();
687 quantise_left_y (dy_f);
688 dy_f = check_stemlengths_f (true);
689 if (abs (dy_f) <= epsilon_f)
697 Beam::set_beaming (Beaming_info_list *beaming)
700 for (int i=0; i < stem_count (); i++)
704 if (stem (i)->beam_count (d) < 0)
705 stem (i)->set_beaming (beaming->infos_.elem (i).beams_i_drul_[d], d);
707 while (flip (&d) != LEFT);
714 Beam::multiplicity_i () const
717 for (SCM s = get_elt_property ("stems"); gh_pair_p (s); s = gh_cdr (s))
719 Score_element * sc = unsmob_element (gh_car (s));
721 if (Stem * st = dynamic_cast<Stem*> (sc))
722 m = m >? st->beam_count (LEFT) >? st->beam_count (RIGHT);
728 beams to go with one stem.
733 Beam::stem_beams (Stem *here, Stem *next, Stem *prev) const
735 if ((next && !(next->hpos_f () > here->hpos_f ())) ||
736 (prev && !(prev->hpos_f () < here->hpos_f ())))
737 programming_error ("Beams are not left-to-right");
740 int multiplicity = multiplicity_i();
742 Real staffline_f = paper_l ()->get_var ("stafflinethickness");
743 Real interbeam_f = paper_l ()->interbeam_f (multiplicity);
745 Real beam_f = gh_scm2double (get_elt_property ("beam-thickness"));;
747 Real dy = interbeam_f;
748 Real stemdx = staffline_f;
756 if (!here->first_head ())
758 else if (here->type_i ()== 1)
759 nw_f = paper_l ()->get_var ("wholewidth");
760 else if (here->type_i () == 2)
761 nw_f = paper_l ()->get_var ("notewidth") * 0.8;
763 nw_f = paper_l ()->get_var ("quartwidth");
765 /* half beams extending to the left. */
768 int lhalfs= lhalfs = here->beam_count (LEFT)
769 - prev->beam_count (RIGHT);
770 int lwholebeams= here->beam_count (LEFT) <? prev->beam_count (RIGHT);
772 Half beam should be one note-width,
773 but let's make sure two half-beams never touch
775 Real w = here->hpos_f () - prev->hpos_f ();
778 if (lhalfs) // generates warnings if not
779 a = lookup_l ()->beam (sl, w, beam_f);
780 a.translate (Offset (-w, -w * sl));
781 for (int j = 0; j < lhalfs; j++)
784 b.translate_axis (-get_direction () * dy * (lwholebeams+j), Y_AXIS);
785 leftbeams.add_molecule (b);
791 int rhalfs = here->beam_count (RIGHT) - next->beam_count (LEFT);
792 int rwholebeams = here->beam_count(RIGHT) <? next->beam_count (LEFT);
794 Real w = next->hpos_f () - here->hpos_f ();
795 Molecule a = lookup_l ()->beam (sl, w + stemdx, beam_f);
796 a.translate_axis( - stemdx/2, X_AXIS);
800 SCM gap = get_elt_property ("beam-gap");
801 if (gap != SCM_UNDEFINED)
803 int gap_i = gh_scm2int ( (gap));
804 int nogap = rwholebeams - gap_i;
806 for (; j < nogap; j++)
809 b.translate_axis (-get_direction () * dy * j, Y_AXIS);
810 rightbeams.add_molecule (b);
812 // TODO: notehead widths differ for different types
815 a = lookup_l ()->beam (sl, w + stemdx, beam_f);
818 for (; j < rwholebeams; j++)
821 if (!here->invisible_b ())
822 b.translate (Offset (gap_f, -get_direction () * dy * j));
824 b.translate (Offset (0, -get_direction () * dy * j));
825 rightbeams.add_molecule (b);
830 a = lookup_l ()->beam (sl, w, beam_f);
832 for (; j < rwholebeams + rhalfs; j++)
835 b.translate_axis (-get_direction () * dy * j, Y_AXIS);
836 rightbeams.add_molecule (b);
840 leftbeams.add_molecule (rightbeams);
843 Does beam quanting think of the asymetry of beams?
844 Refpoint is on bottom of symbol. (FIXTHAT) --hwn.