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.
27 #include "chord-tremolo.hh"
30 #include "dimensions.hh"
34 #include "molecule.hh"
35 #include "leastsquares.hh"
37 #include "paper-def.hh"
48 Beam::add_stem (Stem*s)
52 should figure out why this didn't work.
58 set_parent (s, Y_AXIS);
62 s->add_dependency (this);
67 if (!spanned_drul_[LEFT])
74 Beam::get_stem_info (Stem *s)
77 for (int i=0; i < sinfo_.size (); i++)
79 if (sinfo_[i].stem_l_ == s)
87 Beam::do_brew_molecule_p () const
89 Molecule *mol_p = new Molecule;
93 Real x0 = stems_[0]->hpos_f ();
94 for (int j=0; j <stems_.size (); j++)
97 Stem * prev = (j > 0)? stems_[j-1] : 0;
98 Stem * next = (j < stems_.size ()-1) ? stems_[j+1] :0;
100 Molecule sb = stem_beams (i, next, prev);
101 Real x = i->hpos_f ()-x0;
102 sb.translate (Offset (x, (x * slope_f_ + left_y_) *
103 i->staff_line_leading_f ()/2 ));
104 mol_p->add_molecule (sb);
106 mol_p->translate_axis (x0
107 - spanned_drul_[LEFT]->relative_coordinate (0, X_AXIS), X_AXIS);
113 Beam::center () const
115 Stem_info si = sinfo_[0];
117 Real w= (si.stem_l_->note_delta_f () + extent (X_AXIS).length ())/2.0;
118 return Offset (w, ( w* slope_f_) *
119 si.stem_l_->staff_line_leading_f ()/2);
123 Simplistic auto-knees; only consider vertical gap between two
127 Beam::auto_knee (SCM gap, bool interstaff_b)
131 Real internote_f = stems_[0]->staff_line_leading_f ()/2;
132 if (gap != SCM_BOOL_F)
134 int auto_gap_i = gh_scm2int (SCM_CDR (gap));
135 for (int i=1; i < stems_.size (); i++)
137 bool is_b = (bool)(sinfo_[i].interstaff_f_ - sinfo_[i-1].interstaff_f_);
138 int l_y = (int)(stems_[i-1]->chord_start_f () / internote_f)
139 + (int)sinfo_[i-1].interstaff_f_;
140 int r_y = (int)(stems_[i]->chord_start_f () / internote_f)
141 + (int)sinfo_[i].interstaff_f_;
142 int gap_i = r_y - l_y;
145 Forced stem directions are ignored. If you don't want auto-knees,
146 don't set, or unset autoKneeGap/autoInterstaffKneeGap.
148 if ((abs (gap_i) >= auto_gap_i) && (!interstaff_b || is_b))
150 knee_y = (r_y + l_y) / 2;
158 for (int i=0; i < stems_.size (); i++)
160 int y = (int)(stems_[i]->chord_start_f () / internote_f)
161 + (int)sinfo_[i].interstaff_f_;
162 stems_[i]->dir_ = y < knee_y ? UP : DOWN;
163 stems_[i]->set_elt_property (dir_forced_scm_sym, SCM_BOOL_T);
172 if (auto_knee (get_elt_property (auto_interstaff_knee_gap_scm_sym), true))
175 return auto_knee (get_elt_property (auto_knee_gap_scm_sym), false);
180 Beam::do_pre_processing ()
183 urg: it seems that info on whether beam (voice) dir was forced
184 is being junked here?
187 dir_ = get_default_dir ();
189 set_direction (dir_);
193 Beam::do_print () const
196 DEBUG_OUT << "slope_f_ " << slope_f_ << "left ypos " << left_y_;
197 Spanner::do_print ();
202 Beam::do_post_processing ()
204 if (stems_.size () < 2)
206 warning (_ ("beam with less than two stems"));
207 set_elt_property (transparent_scm_sym, SCM_BOOL_T);
214 if auto-knee did its work, most probably stem directions
215 have changed, so we must recalculate all.
217 dir_ = get_default_dir ();
218 set_direction (dir_);
220 /* auto-knees used to only work for slope = 0
221 anyway, should be able to set slope per beam
222 set_elt_property (damping_scm_sym, gh_int2scm(1000));
233 Beam::do_substitute_element_pointer (Score_element*o,Score_element*n)
235 if (Stem * os = dynamic_cast<Stem*> (o))
236 stems_.substitute (os,
237 dynamic_cast<Stem *> (n));
241 Beam::do_width () const
243 return Interval (stems_[0]->hpos_f (),
244 stems_.top ()->hpos_f ());
248 Beam::get_default_dir () const
250 Drul_array<int> total;
251 total[UP] = total[DOWN] = 0;
252 Drul_array<int> count;
253 count[UP] = count[DOWN] = 0;
256 for (int i=0; i <stems_.size (); i++)
259 int current = s->dir_
260 ? (1 + d * s->dir_)/2
261 : s->get_center_distance ((Direction)-d);
269 } while (flip(&d) != DOWN);
272 [Ross] states that the majority of the notes dictates the
273 direction (and not the mean of "center distance")
275 But is that because it really looks better, or because he wants
276 to provide some real simple hands-on rules?
278 We have our doubts, so we simply provide all sensible alternatives.
280 If dir is not determined: up (see stem::get_default_dir ()) */
283 Direction neutral_dir = (Direction)(int)paper_l ()->get_var ("stem_default_neutral_direction");
285 SCM a = get_elt_property (gh_symbol2scm ("beam_dir_algorithm"));
288 if (a == gh_symbol2scm ("majority")) // should get default from paper.
289 beam_dir = (count[UP] == count[DOWN]) ? neutral_dir
290 : (count[UP] > count[DOWN]) ? UP : DOWN;
291 else if (a == gh_symbol2scm ("mean"))
292 // mean center distance
293 beam_dir = (total[UP] == total[DOWN]) ? neutral_dir
294 : (total[UP] > total[DOWN]) ? UP : DOWN;
295 else if (a == gh_symbol2scm ("median"))
297 // median center distance
298 if (count[DOWN] && count[UP])
300 beam_dir = (total[UP] / count[UP] == total[DOWN] / count[DOWN])
302 : (total[UP] / count[UP] > total[DOWN] / count[DOWN]) ? UP : DOWN;
306 beam_dir = (count[UP] == count[DOWN]) ? neutral_dir
307 : (count[UP] > count[DOWN]) ? UP : DOWN;
315 Beam::set_direction (Direction d)
318 for (int i=0; i <stems_.size (); i++)
321 s->set_elt_property (beam_dir_scm_sym, gh_int2scm (d));
323 SCM force = s->remove_elt_property (dir_forced_scm_sym);
324 if (force == SCM_BOOL_F)
330 See Documentation/tex/fonts.doc
336 assert (sinfo_.size () > 1);
339 for (int i=0; i < sinfo_.size (); i++)
341 l.input.push (Offset (sinfo_[i].x_, sinfo_[i].idealy_f_));
343 l.minimise (slope_f_, left_y_);
347 ugh. Naming: this doesn't check, but sets as well.
351 Beam::check_stemlengths_f (bool set_b)
353 Real interbeam_f = paper_l ()->interbeam_f (multiple_i_);
355 Real beam_f = paper_l ()->get_realvar (beam_thickness_scm_sym);;
356 Real staffline_f = paper_l ()-> get_var ("stafflinethickness");
357 Real epsilon_f = staffline_f / 8;
359 for (int i=0; i < sinfo_.size (); i++)
361 Real y = sinfo_[i].x_ * slope_f_ + left_y_;
364 if (dir_ != sinfo_[i].dir_)
366 Real internote_f = sinfo_[i].stem_l_->staff_line_leading_f ()/2;
367 y -= dir_ * (beam_f / 2
368 + (sinfo_[i].mult_i_ - 1) * interbeam_f) / internote_f;
369 if (!i && sinfo_[i].stem_l_->staff_symbol_l () !=
370 sinfo_.top ().stem_l_->staff_symbol_l ())
371 y += dir_ * (multiple_i_ - (sinfo_[i].stem_l_->flag_i_ - 2) >? 0)
372 * interbeam_f / internote_f;
376 sinfo_[i].stem_l_->set_stemend (y - sinfo_[i].interstaff_f_);
379 if (y > sinfo_[i].maxy_f_)
380 dy_f = dy_f <? sinfo_[i].maxy_f_ - y;
381 if (y < sinfo_[i].miny_f_)
383 // when all too short, normal stems win..
384 if (dy_f < -epsilon_f)
385 warning (_ ("weird beam vertical offset"));
386 dy_f = dy_f >? sinfo_[i].miny_f_ - y;
393 Beam::set_steminfo ()
398 assert (multiple_i_);
400 int total_count_i = 0;
401 int forced_count_i = 0;
402 for (int i=0; i < stems_.size (); i++)
406 s->set_default_extents ();
407 if (s->invisible_b ())
409 if (((int)s->chord_start_f ()) && (s->dir_ != s->get_default_dir ()))
414 bool grace_b = get_elt_property (grace_scm_sym) != SCM_BOOL_F;
415 String type_str = grace_b ? "grace_" : "";
416 int stem_max = (int)rint(paper_l ()->get_var ("stem_max"));
417 Real shorten_f = paper_l ()->get_var (type_str + "forced_stem_shorten"
418 + to_str (multiple_i_ <? stem_max));
421 for (int i=0; i < stems_.size (); i++)
425 Chord tremolo needs to beam over invisible stems of wholes
427 if (!dynamic_cast<Chord_tremolo*> (this))
429 if (s->invisible_b ())
433 Stem_info info (s, multiple_i_);
437 if (info.dir_ == dir_)
439 if (forced_count_i == total_count_i)
440 info.idealy_f_ -= shorten_f;
441 else if (forced_count_i > total_count_i / 2)
442 info.idealy_f_ -= shorten_f / 2;
449 Beam::calculate_slope ()
452 slope_f_ = left_y_ = 0;
453 else if (sinfo_[0].idealy_f_ == sinfo_.top ().idealy_f_)
456 left_y_ = sinfo_[0].idealy_f_;
462 Real solved_slope_f = slope_f_;
465 steep slope running against lengthened stem is suspect
467 Real dx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
469 // urg, these y internote-y-dimensions
470 Real internote_f = stems_[0]->staff_line_leading_f ()/2;
472 Real lengthened = paper_l ()->get_var ("beam_lengthened") / internote_f;
473 Real steep = paper_l ()->get_var ("beam_steep_slope") / internote_f;
474 if (((left_y_ - sinfo_[0].idealy_f_ > lengthened)
475 && (slope_f_ > steep))
476 || ((left_y_ + slope_f_ * dx_f - sinfo_.top ().idealy_f_ > lengthened)
477 && (slope_f_ < -steep)))
483 This neat trick is by Werner Lemberg,
484 damped = tanh (slope_f_)
485 corresponds with some tables in [Wanske]
487 SCM damp = remove_elt_property (damping_scm_sym);
488 int damping = 1; // ugh.
489 if (damp!= SCM_BOOL_F)
490 damping = gh_int2scm (SCM_CDR(damp));
493 slope_f_ = 0.6 * tanh (slope_f_) / damping;
497 Real damped_slope_dy_f = (solved_slope_f - slope_f_) * dx_f / 2;
498 left_y_ += damped_slope_dy_f;
509 [Ross] (simplification of)
510 Try to set slope_f_ complying with y-span of:
512 - beam_f / 2 + staffline_f / 2
513 - beam_f + staffline_f
517 SCM q = get_elt_property (gh_symbol2scm ("slope_quantisation"));
520 if (q == gh_symbol2scm ("none"))
523 Real interline_f = stems_[0]->staff_line_leading_f ();
524 Real internote_f = interline_f / 2;
525 Real staffline_f = paper_l ()->get_var ("stafflinethickness");
526 Real beam_f = paper_l ()->get_realvar (beam_thickness_scm_sym);;
528 Real dx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
530 // dim(y) = internote; so slope = (y/internote)/x
531 Real dy_f = dx_f * abs (slope_f_ * internote_f);
535 Array<Real> allowed_fraction (3);
536 allowed_fraction[0] = 0;
537 allowed_fraction[1] = (beam_f / 2 + staffline_f / 2);
538 allowed_fraction[2] = (beam_f + staffline_f);
541 Interval iv = quantise_iv (allowed_fraction, interline_f, dy_f);
542 quanty_f = (dy_f - iv[SMALLER] <= iv[BIGGER] - dy_f)
547 slope_f_ = (quanty_f / dx_f) / internote_f * sign (slope_f_);
552 Prevent interference from stafflines and beams. See Documentation/tex/fonts.doc
556 Beam::quantise_left_y (bool extend_b)
559 we only need to quantise the start of the beam as dy is quantised too
560 if extend_b then stems must *not* get shorter
562 SCM q = get_elt_property (gh_symbol2scm ("slope_quantisation"));
566 ----------------------------------------------------------
570 --------------########------------------------------------
573 hang straddle sit inter hang
576 Real space = stems_[0]->staff_line_leading_f ();
577 Real internote_f = space /2;
578 Real staffline_f = paper_l ()->get_var ("stafflinethickness");
579 Real beam_f = paper_l ()->get_realvar (beam_thickness_scm_sym);;
583 it would be nice to have all allowed positions in a runtime matrix:
584 (multiplicity, minimum_beam_dy, maximum_beam_dy)
588 Real sit = beam_f / 2 - staffline_f / 2;
589 Real hang = space - beam_f / 2 + staffline_f / 2;
592 Put all allowed positions into an array.
593 Whether a position is allowed or not depends on
594 strictness of quantisation, multiplicity and direction.
596 For simplicity, we'll assume dir = UP and correct if
597 dir = DOWN afterwards.
599 // isn't this asymmetric ? --hwn
601 // dim(left_y_) = internote
602 Real dy_f = dir_ * left_y_ * internote_f;
604 Real beamdx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
605 Real beamdy_f = beamdx_f * slope_f_ * internote_f;
607 Array<Real> allowed_position;
608 if (q == gh_symbol2scm ("normal"))
610 if ((multiple_i_ <= 2) || (abs (beamdy_f) >= staffline_f / 2))
611 allowed_position.push (straddle);
612 if ((multiple_i_ <= 1) || (abs (beamdy_f) >= staffline_f / 2))
613 allowed_position.push (sit);
614 allowed_position.push (hang);
616 else if (q == gh_symbol2scm ("traditional"))
618 // TODO: check and fix TRADITIONAL
619 if ((multiple_i_ <= 2) || (abs (beamdy_f) >= staffline_f / 2))
620 allowed_position.push (straddle);
621 if ((multiple_i_ <= 1) && (beamdy_f <= staffline_f / 2))
622 allowed_position.push (sit);
623 if (beamdy_f >= -staffline_f / 2)
624 allowed_position.push (hang);
628 Interval iv = quantise_iv (allowed_position, space, dy_f);
630 Real quanty_f = dy_f - iv[SMALLER] <= iv[BIGGER] - dy_f ? iv[SMALLER] : iv[BIGGER];
632 quanty_f = iv[BIGGER];
634 // dim(left_y_) = internote
635 left_y_ = dir_ * quanty_f / internote_f;
639 Beam::set_stemlens ()
641 Real staffline_f = paper_l ()->get_var ("stafflinethickness");
643 Real epsilon_f = staffline_f / 8;
646 // je bent zelf eng --hwn.
647 Real dy_f = check_stemlengths_f (false);
648 for (int i = 0; i < 2; i++) // 2 ?
650 left_y_ += dy_f * dir_;
651 quantise_left_y (dy_f);
652 dy_f = check_stemlengths_f (true);
653 if (abs (dy_f) <= epsilon_f)
661 Beam::set_beaming (Beaming_info_list *beaming)
664 for (int i=0; i < stems_.size (); i++)
668 if (stems_[i]->beams_i_drul_[d] < 0)
669 stems_[i]->beams_i_drul_[d] = beaming->infos_.elem (i).beams_i_drul_[d];
671 while (flip (&d) != LEFT);
677 Beam::do_add_processing ()
679 for (int i=0; i < stems_.size () ; i++)
683 multiple_i_ = multiple_i_ >? stems_[i]->beams_i_drul_[d];
684 } while ((flip (&d)) != LEFT);
689 stems_[0]->beams_i_drul_[LEFT] =0;
690 stems_.top()->beams_i_drul_[RIGHT] =0;
697 beams to go with one stem.
702 Beam::stem_beams (Stem *here, Stem *next, Stem *prev) const
704 if ((next && !(next->hpos_f () > here->hpos_f ())) ||
705 (prev && !(prev->hpos_f () < here->hpos_f ())))
706 programming_error ("Beams are not left-to-right");
708 Real staffline_f = paper_l ()->get_var ("stafflinethickness");
709 Real interbeam_f = paper_l ()->interbeam_f (multiple_i_);
711 Real internote_f = here->staff_line_leading_f ()/2;
712 Real beam_f = paper_l ()->get_realvar (beam_thickness_scm_sym);;
714 Real dy = interbeam_f;
715 Real stemdx = staffline_f;
716 Real sl = slope_f_* internote_f;
723 if (!here->head_l_arr_.size ())
725 else if (here->type_i ()== 1)
726 nw_f = paper_l ()->get_var ("wholewidth");
727 else if (here->type_i () == 2)
728 nw_f = paper_l ()->get_var ("notewidth") * 0.8;
730 nw_f = paper_l ()->get_var ("quartwidth");
732 /* half beams extending to the left. */
735 int lhalfs= lhalfs = here->beams_i_drul_[LEFT] - prev->beams_i_drul_[RIGHT] ;
736 int lwholebeams= here->beams_i_drul_[LEFT] <? prev->beams_i_drul_[RIGHT] ;
738 Half beam should be one note-width,
739 but let's make sure two half-beams never touch
741 Real w = here->hpos_f () - prev->hpos_f ();
744 if (lhalfs) // generates warnings if not
745 a = lookup_l ()->beam (sl, w, beam_f);
746 a.translate (Offset (-w, -w * sl));
747 for (int j = 0; j < lhalfs; j++)
750 b.translate_axis (-dir_ * dy * (lwholebeams+j), Y_AXIS);
751 leftbeams.add_molecule (b);
757 int rhalfs = here->beams_i_drul_[RIGHT] - next->beams_i_drul_[LEFT];
758 int rwholebeams = here->beams_i_drul_[RIGHT] <? next->beams_i_drul_[LEFT];
760 Real w = next->hpos_f () - here->hpos_f ();
761 Molecule a = lookup_l ()->beam (sl, w + stemdx, beam_f);
762 a.translate_axis( - stemdx/2, X_AXIS);
766 SCM gap = get_elt_property (beam_gap_scm_sym);
767 if (gap != SCM_BOOL_F)
769 int gap_i = gh_scm2int (SCM_CDR (gap));
770 int nogap = rwholebeams - gap_i;
772 for (; j < nogap; j++)
775 b.translate_axis (-dir_ * dy * j, Y_AXIS);
776 rightbeams.add_molecule (b);
778 // TODO: notehead widths differ for different types
781 a = lookup_l ()->beam (sl, w + stemdx, beam_f);
784 for (; j < rwholebeams; j++)
787 if (!here->invisible_b ())
788 b.translate (Offset (gap_f, -dir_ * dy * j));
790 b.translate (Offset (0, -dir_ * dy * j));
791 rightbeams.add_molecule (b);
796 a = lookup_l ()->beam (sl, w, beam_f);
798 for (; j < rwholebeams + rhalfs; j++)
801 b.translate_axis (-dir_ * dy * j, Y_AXIS);
802 rightbeams.add_molecule (b);
806 leftbeams.add_molecule (rightbeams);
809 Does beam quanting think of the asymetry of beams?
810 Refpoint is on bottom of symbol. (FIXTHAT) --hwn.