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"
44 quantisation_ = NORMAL;
49 Beam::add_stem (Stem*s)
53 should figure out why this didn't work.
59 set_parent (s, Y_AXIS);
63 s->add_dependency (this);
68 if (!spanned_drul_[LEFT])
75 Beam::get_stem_info (Stem *s)
78 for (int i=0; i < sinfo_.size (); i++)
80 if (sinfo_[i].stem_l_ == s)
88 Beam::do_brew_molecule_p () const
90 Molecule *mol_p = new Molecule;
94 Real x0 = stems_[0]->hpos_f ();
95 for (int j=0; j <stems_.size (); j++)
98 Stem * prev = (j > 0)? stems_[j-1] : 0;
99 Stem * next = (j < stems_.size ()-1) ? stems_[j+1] :0;
101 Molecule sb = stem_beams (i, next, prev);
102 Real x = i->hpos_f ()-x0;
103 sb.translate (Offset (x, (x * slope_f_ + left_y_) *
104 i->staff_line_leading_f ()/2 ));
105 mol_p->add_molecule (sb);
107 mol_p->translate_axis (x0
108 - spanned_drul_[LEFT]->relative_coordinate (0, X_AXIS), X_AXIS);
114 Beam::center () const
116 Stem_info si = sinfo_[0];
118 Real w= (si.stem_l_->note_delta_f () + extent (X_AXIS).length ())/2.0;
119 return Offset (w, ( w* slope_f_) *
120 si.stem_l_->staff_line_leading_f ()/2);
124 Simplistic auto-knees; only consider vertical gap between two
128 Beam::auto_knee (SCM gap, bool interstaff_b)
132 Real internote_f = stems_[0]->staff_line_leading_f ()/2;
133 if (gap != SCM_BOOL_F)
135 int auto_gap_i = gh_scm2int (SCM_CDR (gap));
136 for (int i=1; i < stems_.size (); i++)
138 bool is_b = (bool)(sinfo_[i].interstaff_f_ - sinfo_[i-1].interstaff_f_);
139 int l_y = (int)(stems_[i-1]->chord_start_f () / internote_f)
140 + (int)sinfo_[i-1].interstaff_f_;
141 int r_y = (int)(stems_[i]->chord_start_f () / internote_f)
142 + (int)sinfo_[i].interstaff_f_;
143 int gap_i = r_y - l_y;
146 Forced stem directions are ignored. If you don't want auto-knees,
147 don't set, or unset autoKneeGap/autoInterstaffKneeGap.
149 if ((abs (gap_i) >= auto_gap_i) && (!interstaff_b || is_b))
151 knee_y = (r_y + l_y) / 2;
159 for (int i=0; i < stems_.size (); i++)
161 int y = (int)(stems_[i]->chord_start_f () / internote_f)
162 + (int)sinfo_[i].interstaff_f_;
163 stems_[i]->dir_ = y < knee_y ? UP : DOWN;
164 stems_[i]->set_elt_property (dir_forced_scm_sym, SCM_BOOL_T);
173 if (auto_knee (get_elt_property (auto_interstaff_knee_gap_scm_sym), true))
176 return auto_knee (get_elt_property (auto_knee_gap_scm_sym), false);
181 Beam::do_pre_processing ()
184 urg: it seems that info on whether beam (voice) dir was forced
185 is being junked here?
188 dir_ = get_default_dir ();
190 set_direction (dir_);
194 Beam::do_print () const
197 DEBUG_OUT << "slope_f_ " << slope_f_ << "left ypos " << left_y_;
198 Spanner::do_print ();
203 Beam::do_post_processing ()
205 if (stems_.size () < 2)
207 warning (_ ("beam with less than two stems"));
208 set_elt_property (transparent_scm_sym, SCM_BOOL_T);
215 if auto-knee did its work, most probably stem directions
216 have changed, so we must recalculate all.
218 dir_ = get_default_dir ();
219 set_direction (dir_);
221 /* auto-knees used to only work for slope = 0
222 anyway, should be able to set slope per beam
223 set_elt_property (damping_scm_sym, gh_int2scm(1000));
234 Beam::do_substitute_element_pointer (Score_element*o,Score_element*n)
236 if (Stem * os = dynamic_cast<Stem*> (o))
237 stems_.substitute (os,
238 dynamic_cast<Stem *> (n));
242 Beam::do_width () const
244 return Interval (stems_[0]->hpos_f (),
245 stems_.top ()->hpos_f ());
249 Beam::get_default_dir () const
251 Drul_array<int> total;
252 total[UP] = total[DOWN] = 0;
253 Drul_array<int> count;
254 count[UP] = count[DOWN] = 0;
257 for (int i=0; i <stems_.size (); i++)
260 int current = s->dir_
261 ? (1 + d * s->dir_)/2
262 : s->get_center_distance ((Direction)-d);
270 } while (flip(&d) != DOWN);
273 [Ross] states that the majority of the notes dictates the
274 direction (and not the mean of "center distance")
276 But is that because it really looks better, or because he
277 wants to provide some real simple hands-on rules.
279 We have our doubts, so we simply provide all sensible alternatives.
281 If dir is not determined: up (see stem::get_default_dir ())
285 Direction neutral_dir = (Direction)(int)paper_l ()->get_var ("stem_default_neutral_direction");
287 Dir_algorithm a = (Dir_algorithm)rint(paper_l ()->get_var ("beam_dir_algorithm"));
291 beam_dir = (count[UP] == count[DOWN]) ? neutral_dir
292 : (count[UP] > count[DOWN]) ? UP : DOWN;
295 // mean center distance
296 beam_dir = (total[UP] == total[DOWN]) ? neutral_dir
297 : (total[UP] > total[DOWN]) ? UP : DOWN;
301 // median center distance
302 if (!count[DOWN] || !count[UP])
304 beam_dir = (count[UP] == count[DOWN]) ? neutral_dir
305 : (count[UP] > count[DOWN]) ? UP : DOWN;
309 beam_dir = (total[UP] / count[UP] == total[DOWN] / count[DOWN])
311 : (total[UP] / count[UP] > total[DOWN] / count[DOWN]) ? UP : DOWN;
319 Beam::set_direction (Direction d)
322 for (int i=0; i <stems_.size (); i++)
325 s->set_elt_property (beam_dir_scm_sym, gh_int2scm (d));
327 SCM force = s->remove_elt_property (dir_forced_scm_sym);
328 if (force == SCM_BOOL_F)
334 See Documentation/tex/fonts.doc
340 assert (sinfo_.size () > 1);
341 DEBUG_OUT << "Beam::solve_slope: \n";
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 = paper_l ()->beam_thickness_f ();
361 Real staffline_f = paper_l ()->rule_thickness ();
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 (dir_ != sinfo_[i].dir_)
371 Real internote_f = sinfo_[i].stem_l_->staff_line_leading_f ()/2;
372 y -= dir_ * (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 += dir_ * (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_);
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 < stems_.size (); i++)
411 s->set_default_extents ();
412 if (s->invisible_b ())
414 if (((int)s->chord_start_f ()) && (s->dir_ != s->get_default_dir ()))
419 bool grace_b = get_elt_property (grace_scm_sym) != SCM_BOOL_F;
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 < stems_.size (); i++)
430 Chord tremolo needs to beam over invisible stems of wholes
432 if (!dynamic_cast<Chord_tremolo*> (this))
434 if (s->invisible_b ())
438 Stem_info info (s, multiple_i_);
442 if (info.dir_ == dir_)
444 if (forced_count_i == total_count_i)
445 info.idealy_f_ -= shorten_f;
446 else if (forced_count_i > total_count_i / 2)
447 info.idealy_f_ -= shorten_f / 2;
454 Beam::calculate_slope ()
457 slope_f_ = left_y_ = 0;
458 else if (sinfo_[0].idealy_f_ == sinfo_.top ().idealy_f_)
461 left_y_ = sinfo_[0].idealy_f_;
467 Real solved_slope_f = slope_f_;
470 steep slope running against lengthened stem is suspect
472 Real dx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
474 // urg, these y internote-y-dimensions
475 Real internote_f = stems_[0]->staff_line_leading_f ()/2;
477 Real lengthened = paper_l ()->get_var ("beam_lengthened") / internote_f;
478 Real steep = paper_l ()->get_var ("beam_steep_slope") / internote_f;
479 if (((left_y_ - sinfo_[0].idealy_f_ > lengthened)
480 && (slope_f_ > steep))
481 || ((left_y_ + slope_f_ * dx_f - sinfo_.top ().idealy_f_ > lengthened)
482 && (slope_f_ < -steep)))
488 This neat trick is by Werner Lemberg,
489 damped = tanh (slope_f_)
490 corresponds with some tables in [Wanske]
492 SCM damp = remove_elt_property (damping_scm_sym);
493 int damping = 1; // ugh.
494 if (damp!= SCM_BOOL_F)
495 damping = gh_int2scm (SCM_CDR(damp));
498 slope_f_ = 0.6 * tanh (slope_f_) / damping;
502 Real damped_slope_dy_f = (solved_slope_f - slope_f_) * dx_f / 2;
503 left_y_ += damped_slope_dy_f;
514 [Ross] (simplification of)
515 Try to set slope_f_ complying with y-span of:
517 - beam_f / 2 + staffline_f / 2
518 - beam_f + staffline_f
522 if (quantisation_ <= NONE)
525 Real interline_f = stems_[0]->staff_line_leading_f ();
526 Real internote_f = interline_f / 2;
527 Real staffline_f = paper_l ()->rule_thickness ();
528 Real beam_f = paper_l ()->beam_thickness_f ();
530 Real dx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
532 // dim(y) = internote; so slope = (y/internote)/x
533 Real dy_f = dx_f * abs (slope_f_ * internote_f);
537 /* UGR. ICE in 2.8.1; bugreport filed. */
538 Array<Real> allowed_fraction (3);
539 allowed_fraction[0] = 0;
540 allowed_fraction[1] = (beam_f / 2 + staffline_f / 2);
541 allowed_fraction[2] = (beam_f + staffline_f);
544 Interval iv = quantise_iv (allowed_fraction, interline_f, dy_f);
545 quanty_f = (dy_f - iv[SMALLER] <= iv[BIGGER] - dy_f)
550 slope_f_ = (quanty_f / dx_f) / internote_f * sign (slope_f_);
553 static int test_pos = 0;
558 Prevent interference from stafflines and beams. See Documentation/tex/fonts.doc
562 Beam::quantise_left_y (bool extend_b)
565 we only need to quantise the start of the beam as dy is quantised too
566 if extend_b then stems must *not* get shorter
569 if (quantisation_ <= NONE)
573 ----------------------------------------------------------
577 --------------########------------------------------------
580 hang straddle sit inter hang
583 Real space = stems_[0]->staff_line_leading_f ();
584 Real internote_f = space /2;
585 Real staffline_f = paper_l ()->rule_thickness ();
586 Real beam_f = paper_l ()->beam_thickness_f ();
590 it would be nice to have all allowed positions in a runtime matrix:
591 (multiplicity, minimum_beam_dy, maximum_beam_dy)
595 Real sit = beam_f / 2 - staffline_f / 2;
596 Real inter = space / 2;
597 Real hang = space - beam_f / 2 + staffline_f / 2;
600 Put all allowed positions into an array.
601 Whether a position is allowed or not depends on
602 strictness of quantisation, multiplicity and direction.
604 For simplicity, we'll assume dir = UP and correct if
605 dir = DOWN afterwards.
608 // dim(left_y_) = internote
609 Real dy_f = dir_ * left_y_ * internote_f;
611 Real beamdx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
612 Real beamdy_f = beamdx_f * slope_f_ * internote_f;
614 Array<Real> allowed_position;
615 if (quantisation_ != TEST)
617 if (quantisation_ <= NORMAL)
619 if ((multiple_i_ <= 2) || (abs (beamdy_f) >= staffline_f / 2))
620 allowed_position.push (straddle);
621 if ((multiple_i_ <= 1) || (abs (beamdy_f) >= staffline_f / 2))
622 allowed_position.push (sit);
623 allowed_position.push (hang);
626 // TODO: check and fix TRADITIONAL
628 if ((multiple_i_ <= 2) || (abs (beamdy_f) >= staffline_f / 2))
629 allowed_position.push (straddle);
630 if ((multiple_i_ <= 1) && (beamdy_f <= staffline_f / 2))
631 allowed_position.push (sit);
632 if (beamdy_f >= -staffline_f / 2)
633 allowed_position.push (hang);
640 allowed_position.push (hang);
641 cout << "hang" << hang << "\n";
643 else if (test_pos==1)
645 allowed_position.push (straddle);
646 cout << "straddle" << straddle << endl;
648 else if (test_pos==2)
650 allowed_position.push (sit);
651 cout << "sit" << sit << endl;
653 else if (test_pos==3)
655 allowed_position.push (inter);
656 cout << "inter" << inter << endl;
660 Interval iv = quantise_iv (allowed_position, space, dy_f);
662 Real quanty_f = dy_f - iv[SMALLER] <= iv[BIGGER] - dy_f ? iv[SMALLER] : iv[BIGGER];
664 quanty_f = iv[BIGGER];
666 // dim(left_y_) = internote
667 left_y_ = dir_ * quanty_f / internote_f;
671 Beam::set_stemlens ()
673 Real staffline_f = paper_l ()->rule_thickness ();
675 Real epsilon_f = staffline_f / 8;
678 // je bent zelf eng --hwn.
679 Real dy_f = check_stemlengths_f (false);
680 for (int i = 0; i < 2; i++)
682 left_y_ += dy_f * dir_;
683 quantise_left_y (dy_f);
684 dy_f = check_stemlengths_f (true);
685 if (abs (dy_f) <= epsilon_f)
696 Beam::set_beaming (Beaming_info_list *beaming)
699 for (int i=0; i < stems_.size (); i++)
703 if (stems_[i]->beams_i_drul_[d] < 0)
704 stems_[i]->beams_i_drul_[d] = beaming->infos_.elem (i).beams_i_drul_[d];
706 while (flip (&d) != LEFT);
712 Beam::do_add_processing ()
714 for (int i=0; i < stems_.size () ; i++)
718 multiple_i_ = multiple_i_ >? stems_[i]->beams_i_drul_[d];
719 } while ((flip (&d)) != LEFT);
724 stems_[0]->beams_i_drul_[LEFT] =0;
725 stems_.top()->beams_i_drul_[RIGHT] =0;
732 beams to go with one stem.
735 Beam::stem_beams (Stem *here, Stem *next, Stem *prev) const
737 if ((next && !(next->hpos_f () > here->hpos_f ())) ||
738 (prev && !(prev->hpos_f () < here->hpos_f ())))
739 programming_error ("Beams are not left-to-right");
741 Real staffline_f = paper_l ()->rule_thickness ();
742 Real interbeam_f = paper_l ()->interbeam_f (multiple_i_);
744 Real internote_f = here->staff_line_leading_f ()/2;
745 Real beam_f = paper_l ()->beam_thickness_f ();
747 Real dy = interbeam_f;
748 Real stemdx = staffline_f;
749 Real sl = slope_f_* internote_f;
750 lookup_l ()->beam (sl, 20 PT, 1 PT);
757 if (!here->head_l_arr_.size ())
759 else if (here->type_i ()== 1)
760 nw_f = paper_l ()->get_var ("wholewidth");
761 else if (here->type_i () == 2)
762 nw_f = paper_l ()->note_width () * 0.8;
764 nw_f = paper_l ()->get_var ("quartwidth");
766 /* half beams extending to the left. */
769 int lhalfs= lhalfs = here->beams_i_drul_[LEFT] - prev->beams_i_drul_[RIGHT] ;
770 int lwholebeams= here->beams_i_drul_[LEFT] <? prev->beams_i_drul_[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 (-dir_ * dy * (lwholebeams+j), Y_AXIS);
785 leftbeams.add_molecule (b);
791 int rhalfs = here->beams_i_drul_[RIGHT] - next->beams_i_drul_[LEFT];
792 int rwholebeams = here->beams_i_drul_[RIGHT] <? next->beams_i_drul_[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_scm_sym);
801 if (gap != SCM_BOOL_F)
803 int gap_i = gh_scm2int (SCM_CDR (gap));
804 int nogap = rwholebeams - gap_i;
806 for (; j < nogap; j++)
809 b.translate_axis (-dir_ * 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, -dir_ * dy * j));
824 b.translate (Offset (0, -dir_ * 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 (-dir_ * 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.