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 wants
277 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 ()) */
284 Direction neutral_dir = (Direction)(int)paper_l ()->get_var ("stem_default_neutral_direction");
286 Dir_algorithm a = (Dir_algorithm)rint(paper_l ()->get_var ("beam_dir_algorithm"));
290 beam_dir = (count[UP] == count[DOWN]) ? neutral_dir
291 : (count[UP] > count[DOWN]) ? UP : DOWN;
294 // mean center distance
295 beam_dir = (total[UP] == total[DOWN]) ? neutral_dir
296 : (total[UP] > total[DOWN]) ? UP : DOWN;
300 // median center distance
301 if (!count[DOWN] || !count[UP])
303 beam_dir = (count[UP] == count[DOWN]) ? neutral_dir
304 : (count[UP] > count[DOWN]) ? UP : DOWN;
308 beam_dir = (total[UP] / count[UP] == total[DOWN] / count[DOWN])
310 : (total[UP] / count[UP] > total[DOWN] / count[DOWN]) ? UP : DOWN;
318 Beam::set_direction (Direction d)
321 for (int i=0; i <stems_.size (); i++)
324 s->set_elt_property (beam_dir_scm_sym, gh_int2scm (d));
326 SCM force = s->remove_elt_property (dir_forced_scm_sym);
327 if (force == SCM_BOOL_F)
333 See Documentation/tex/fonts.doc
339 assert (sinfo_.size () > 1);
342 for (int i=0; i < sinfo_.size (); i++)
344 l.input.push (Offset (sinfo_[i].x_, sinfo_[i].idealy_f_));
346 l.minimise (slope_f_, left_y_);
350 ugh. Naming: this doesn't check, but sets as well.
354 Beam::check_stemlengths_f (bool set_b)
356 Real interbeam_f = paper_l ()->interbeam_f (multiple_i_);
358 Real beam_f = paper_l ()->get_realvar (beam_thickness_scm_sym);;
359 Real staffline_f = paper_l ()-> get_var ("stafflinethickness");
360 Real epsilon_f = staffline_f / 8;
362 for (int i=0; i < sinfo_.size (); i++)
364 Real y = sinfo_[i].x_ * slope_f_ + left_y_;
367 if (dir_ != sinfo_[i].dir_)
369 Real internote_f = sinfo_[i].stem_l_->staff_line_leading_f ()/2;
370 y -= dir_ * (beam_f / 2
371 + (sinfo_[i].mult_i_ - 1) * interbeam_f) / internote_f;
372 if (!i && sinfo_[i].stem_l_->staff_symbol_l () !=
373 sinfo_.top ().stem_l_->staff_symbol_l ())
374 y += dir_ * (multiple_i_ - (sinfo_[i].stem_l_->flag_i_ - 2) >? 0)
375 * interbeam_f / internote_f;
379 sinfo_[i].stem_l_->set_stemend (y - sinfo_[i].interstaff_f_);
382 if (y > sinfo_[i].maxy_f_)
383 dy_f = dy_f <? sinfo_[i].maxy_f_ - y;
384 if (y < sinfo_[i].miny_f_)
386 // when all too short, normal stems win..
387 if (dy_f < -epsilon_f)
388 warning (_ ("weird beam vertical offset"));
389 dy_f = dy_f >? sinfo_[i].miny_f_ - y;
396 Beam::set_steminfo ()
401 assert (multiple_i_);
403 int total_count_i = 0;
404 int forced_count_i = 0;
405 for (int i=0; i < stems_.size (); i++)
409 s->set_default_extents ();
410 if (s->invisible_b ())
412 if (((int)s->chord_start_f ()) && (s->dir_ != s->get_default_dir ()))
417 bool grace_b = get_elt_property (grace_scm_sym) != SCM_BOOL_F;
418 String type_str = grace_b ? "grace_" : "";
419 int stem_max = (int)rint(paper_l ()->get_var ("stem_max"));
420 Real shorten_f = paper_l ()->get_var (type_str + "forced_stem_shorten"
421 + to_str (multiple_i_ <? stem_max));
424 for (int i=0; i < stems_.size (); i++)
428 Chord tremolo needs to beam over invisible stems of wholes
430 if (!dynamic_cast<Chord_tremolo*> (this))
432 if (s->invisible_b ())
436 Stem_info info (s, multiple_i_);
440 if (info.dir_ == dir_)
442 if (forced_count_i == total_count_i)
443 info.idealy_f_ -= shorten_f;
444 else if (forced_count_i > total_count_i / 2)
445 info.idealy_f_ -= shorten_f / 2;
452 Beam::calculate_slope ()
455 slope_f_ = left_y_ = 0;
456 else if (sinfo_[0].idealy_f_ == sinfo_.top ().idealy_f_)
459 left_y_ = sinfo_[0].idealy_f_;
465 Real solved_slope_f = slope_f_;
468 steep slope running against lengthened stem is suspect
470 Real dx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
472 // urg, these y internote-y-dimensions
473 Real internote_f = stems_[0]->staff_line_leading_f ()/2;
475 Real lengthened = paper_l ()->get_var ("beam_lengthened") / internote_f;
476 Real steep = paper_l ()->get_var ("beam_steep_slope") / internote_f;
477 if (((left_y_ - sinfo_[0].idealy_f_ > lengthened)
478 && (slope_f_ > steep))
479 || ((left_y_ + slope_f_ * dx_f - sinfo_.top ().idealy_f_ > lengthened)
480 && (slope_f_ < -steep)))
486 This neat trick is by Werner Lemberg,
487 damped = tanh (slope_f_)
488 corresponds with some tables in [Wanske]
490 SCM damp = remove_elt_property (damping_scm_sym);
491 int damping = 1; // ugh.
492 if (damp!= SCM_BOOL_F)
493 damping = gh_int2scm (SCM_CDR(damp));
496 slope_f_ = 0.6 * tanh (slope_f_) / damping;
500 Real damped_slope_dy_f = (solved_slope_f - slope_f_) * dx_f / 2;
501 left_y_ += damped_slope_dy_f;
512 [Ross] (simplification of)
513 Try to set slope_f_ complying with y-span of:
515 - beam_f / 2 + staffline_f / 2
516 - beam_f + staffline_f
520 if (quantisation_ <= 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 /* UGR. ICE in 2.8.1; bugreport filed. */
536 Array<Real> allowed_fraction (3);
537 allowed_fraction[0] = 0;
538 allowed_fraction[1] = (beam_f / 2 + staffline_f / 2);
539 allowed_fraction[2] = (beam_f + staffline_f);
542 Interval iv = quantise_iv (allowed_fraction, interline_f, dy_f);
543 quanty_f = (dy_f - iv[SMALLER] <= iv[BIGGER] - dy_f)
548 slope_f_ = (quanty_f / dx_f) / internote_f * sign (slope_f_);
553 Prevent interference from stafflines and beams. See Documentation/tex/fonts.doc
557 Beam::quantise_left_y (bool extend_b)
560 we only need to quantise the start of the beam as dy is quantised too
561 if extend_b then stems must *not* get shorter
564 if (quantisation_ == NONE)
568 ----------------------------------------------------------
572 --------------########------------------------------------
575 hang straddle sit inter hang
578 Real space = stems_[0]->staff_line_leading_f ();
579 Real internote_f = space /2;
580 Real staffline_f = paper_l ()->get_var ("stafflinethickness");
581 Real beam_f = paper_l ()->get_realvar (beam_thickness_scm_sym);;
585 it would be nice to have all allowed positions in a runtime matrix:
586 (multiplicity, minimum_beam_dy, maximum_beam_dy)
590 Real sit = beam_f / 2 - staffline_f / 2;
591 Real hang = space - beam_f / 2 + staffline_f / 2;
594 Put all allowed positions into an array.
595 Whether a position is allowed or not depends on
596 strictness of quantisation, multiplicity and direction.
598 For simplicity, we'll assume dir = UP and correct if
599 dir = DOWN afterwards.
601 // isn't this asymmetric ? --hwn
603 // dim(left_y_) = internote
604 Real dy_f = dir_ * left_y_ * internote_f;
606 Real beamdx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
607 Real beamdy_f = beamdx_f * slope_f_ * internote_f;
609 Array<Real> allowed_position;
610 if (quantisation_ <= NORMAL)
612 if ((multiple_i_ <= 2) || (abs (beamdy_f) >= staffline_f / 2))
613 allowed_position.push (straddle);
614 if ((multiple_i_ <= 1) || (abs (beamdy_f) >= staffline_f / 2))
615 allowed_position.push (sit);
616 allowed_position.push (hang);
619 // TODO: check and fix TRADITIONAL
621 if ((multiple_i_ <= 2) || (abs (beamdy_f) >= staffline_f / 2))
622 allowed_position.push (straddle);
623 if ((multiple_i_ <= 1) && (beamdy_f <= staffline_f / 2))
624 allowed_position.push (sit);
625 if (beamdy_f >= -staffline_f / 2)
626 allowed_position.push (hang);
630 Interval iv = quantise_iv (allowed_position, space, dy_f);
632 Real quanty_f = dy_f - iv[SMALLER] <= iv[BIGGER] - dy_f ? iv[SMALLER] : iv[BIGGER];
634 quanty_f = iv[BIGGER];
636 // dim(left_y_) = internote
637 left_y_ = dir_ * quanty_f / internote_f;
641 Beam::set_stemlens ()
643 Real staffline_f = paper_l ()->get_var ("stafflinethickness");
645 Real epsilon_f = staffline_f / 8;
648 // je bent zelf eng --hwn.
649 Real dy_f = check_stemlengths_f (false);
650 for (int i = 0; i < 2; i++) // 2 ?
652 left_y_ += dy_f * dir_;
653 quantise_left_y (dy_f);
654 dy_f = check_stemlengths_f (true);
655 if (abs (dy_f) <= epsilon_f)
663 Beam::set_beaming (Beaming_info_list *beaming)
666 for (int i=0; i < stems_.size (); i++)
670 if (stems_[i]->beams_i_drul_[d] < 0)
671 stems_[i]->beams_i_drul_[d] = beaming->infos_.elem (i).beams_i_drul_[d];
673 while (flip (&d) != LEFT);
679 Beam::do_add_processing ()
681 for (int i=0; i < stems_.size () ; i++)
685 multiple_i_ = multiple_i_ >? stems_[i]->beams_i_drul_[d];
686 } while ((flip (&d)) != LEFT);
691 stems_[0]->beams_i_drul_[LEFT] =0;
692 stems_.top()->beams_i_drul_[RIGHT] =0;
699 beams to go with one stem.
704 Beam::stem_beams (Stem *here, Stem *next, Stem *prev) const
706 if ((next && !(next->hpos_f () > here->hpos_f ())) ||
707 (prev && !(prev->hpos_f () < here->hpos_f ())))
708 programming_error ("Beams are not left-to-right");
710 Real staffline_f = paper_l ()->get_var ("stafflinethickness");
711 Real interbeam_f = paper_l ()->interbeam_f (multiple_i_);
713 Real internote_f = here->staff_line_leading_f ()/2;
714 Real beam_f = paper_l ()->get_realvar (beam_thickness_scm_sym);;
716 Real dy = interbeam_f;
717 Real stemdx = staffline_f;
718 Real sl = slope_f_* internote_f;
725 if (!here->head_l_arr_.size ())
727 else if (here->type_i ()== 1)
728 nw_f = paper_l ()->get_var ("wholewidth");
729 else if (here->type_i () == 2)
730 nw_f = paper_l ()->get_var ("notewidth") * 0.8;
732 nw_f = paper_l ()->get_var ("quartwidth");
734 /* half beams extending to the left. */
737 int lhalfs= lhalfs = here->beams_i_drul_[LEFT] - prev->beams_i_drul_[RIGHT] ;
738 int lwholebeams= here->beams_i_drul_[LEFT] <? prev->beams_i_drul_[RIGHT] ;
740 Half beam should be one note-width,
741 but let's make sure two half-beams never touch
743 Real w = here->hpos_f () - prev->hpos_f ();
746 if (lhalfs) // generates warnings if not
747 a = lookup_l ()->beam (sl, w, beam_f);
748 a.translate (Offset (-w, -w * sl));
749 for (int j = 0; j < lhalfs; j++)
752 b.translate_axis (-dir_ * dy * (lwholebeams+j), Y_AXIS);
753 leftbeams.add_molecule (b);
759 int rhalfs = here->beams_i_drul_[RIGHT] - next->beams_i_drul_[LEFT];
760 int rwholebeams = here->beams_i_drul_[RIGHT] <? next->beams_i_drul_[LEFT];
762 Real w = next->hpos_f () - here->hpos_f ();
763 Molecule a = lookup_l ()->beam (sl, w + stemdx, beam_f);
764 a.translate_axis( - stemdx/2, X_AXIS);
768 SCM gap = get_elt_property (beam_gap_scm_sym);
769 if (gap != SCM_BOOL_F)
771 int gap_i = gh_scm2int (SCM_CDR (gap));
772 int nogap = rwholebeams - gap_i;
774 for (; j < nogap; j++)
777 b.translate_axis (-dir_ * dy * j, Y_AXIS);
778 rightbeams.add_molecule (b);
780 // TODO: notehead widths differ for different types
783 a = lookup_l ()->beam (sl, w + stemdx, beam_f);
786 for (; j < rwholebeams; j++)
789 if (!here->invisible_b ())
790 b.translate (Offset (gap_f, -dir_ * dy * j));
792 b.translate (Offset (0, -dir_ * dy * j));
793 rightbeams.add_molecule (b);
798 a = lookup_l ()->beam (sl, w, beam_f);
800 for (; j < rwholebeams + rhalfs; j++)
803 b.translate_axis (-dir_ * dy * j, Y_AXIS);
804 rightbeams.add_molecule (b);
808 leftbeams.add_molecule (rightbeams);
811 Does beam quanting think of the asymetry of beams?
812 Refpoint is on bottom of symbol. (FIXTHAT) --hwn.