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 Beam::do_pre_processing ()
127 dir_ = get_default_dir ();
130 set_direction (dir_);
134 Beam::do_print () const
137 DOUT << "slope_f_ " << slope_f_ << "left ypos " << left_y_;
138 Spanner::do_print ();
143 Beam::do_post_processing ()
145 if (stems_.size () < 2)
147 warning (_ ("beam with less than two stems"));
148 set_elt_property (transparent_scm_sym, SCM_BOOL_T);
156 Beam::do_substitute_element_pointer (Score_element*o,Score_element*n)
158 if (Stem * os = dynamic_cast<Stem*> (o))
159 stems_.substitute (os,
160 dynamic_cast<Stem *> (n));
164 Beam::do_width () const
166 return Interval (stems_[0]->hpos_f (),
167 stems_.top ()->hpos_f ());
171 Beam::get_default_dir () const
173 Drul_array<int> total;
174 total[UP] = total[DOWN] = 0;
175 Drul_array<int> count;
176 count[UP] = count[DOWN] = 0;
179 for (int i=0; i <stems_.size (); i++)
182 int current = s->dir_
183 ? (1 + d * s->dir_)/2
184 : s->get_center_distance ((Direction)-d);
192 } while (flip(&d) != DOWN);
195 [Ross] states that the majority of the notes dictates the
196 direction (and not the mean of "center distance")
198 But is that because it really looks better, or because he
199 wants to provide some real simple hands-on rules.
201 We have our doubts, so we simply provide all sensible alternatives.
203 If dir is not determined: up (see stem::get_default_dir ())
207 Direction neutral_dir = (Direction)(int)paper_l ()->get_var ("stem_default_neutral_direction");
209 Dir_algorithm a = (Dir_algorithm)rint(paper_l ()->get_var ("beam_dir_algorithm"));
213 beam_dir = (count[UP] == count[DOWN]) ? neutral_dir
214 : (count[UP] > count[DOWN]) ? UP : DOWN;
217 // mean center distance
218 beam_dir = (total[UP] == total[DOWN]) ? neutral_dir
219 : (total[UP] > total[DOWN]) ? UP : DOWN;
223 // median center distance
224 if (!count[DOWN] || !count[UP])
226 beam_dir = (count[UP] == count[DOWN]) ? neutral_dir
227 : (count[UP] > count[DOWN]) ? UP : DOWN;
231 beam_dir = (total[UP] / count[UP] == total[DOWN] / count[DOWN])
233 : (total[UP] / count[UP] > total[DOWN] / count[DOWN]) ? UP : DOWN;
241 Beam::set_direction (Direction d)
244 for (int i=0; i <stems_.size (); i++)
247 s->set_elt_property (beam_dir_scm_sym, gh_int2scm (d));
249 SCM force = s->remove_elt_property (dir_forced_scm_sym);
250 if (force == SCM_BOOL_F)
256 See Documentation/tex/fonts.doc
262 assert (sinfo_.size () > 1);
263 DOUT << "Beam::solve_slope: \n";
266 for (int i=0; i < sinfo_.size (); i++)
268 l.input.push (Offset (sinfo_[i].x_, sinfo_[i].idealy_f_));
270 l.minimise (slope_f_, left_y_);
274 ugh. Naming: this doesn't check, but sets as well.
278 Beam::check_stemlengths_f (bool set_b)
280 Real interbeam_f = paper_l ()->interbeam_f (multiple_i_);
282 Real beam_f = paper_l ()->beam_thickness_f ();
283 Real staffline_f = paper_l ()->rule_thickness ();
284 Real epsilon_f = staffline_f / 8;
286 for (int i=0; i < sinfo_.size (); i++)
288 Real y = sinfo_[i].x_ * slope_f_ + left_y_;
291 if (dir_ != sinfo_[i].dir_)
293 Real internote_f = sinfo_[i].stem_l_->staff_line_leading_f ()/2;
294 y -= dir_ * (beam_f / 2
295 + (sinfo_[i].mult_i_ - 1) * interbeam_f) / internote_f;
296 if (!i && sinfo_[i].stem_l_->staff_symbol_l () !=
297 sinfo_.top ().stem_l_->staff_symbol_l ())
298 y += dir_ * (multiple_i_ - (sinfo_[i].stem_l_->flag_i_ - 2) >? 0)
299 * interbeam_f / internote_f;
303 sinfo_[i].stem_l_->set_stemend (y - sinfo_[i].interstaff_f_);
306 if (y > sinfo_[i].maxy_f_)
307 dy_f = dy_f <? sinfo_[i].maxy_f_ - y;
308 if (y < sinfo_[i].miny_f_)
310 // when all too short, normal stems win..
311 if (dy_f < -epsilon_f)
312 warning (_ ("weird beam shift, check your knees"));
313 dy_f = dy_f >? sinfo_[i].miny_f_ - y;
320 Beam::set_steminfo ()
325 assert (multiple_i_);
326 int total_count_i = 0;
327 int forced_count_i = 0;
328 for (int i=0; i < stems_.size (); i++)
332 s->set_default_extents ();
333 if (s->invisible_b ())
335 if (((int)s->chord_start_f ()) && (s->dir_ != s->get_default_dir ()))
340 bool grace_b = get_elt_property (grace_scm_sym) != SCM_BOOL_F;
341 String type_str = grace_b ? "grace_" : "";
342 int stem_max = (int)rint(paper_l ()->get_var ("stem_max"));
343 Real shorten_f = paper_l ()->get_var (type_str + "forced_stem_shorten"
344 + to_str (multiple_i_ <? stem_max));
347 for (int i=0; i < stems_.size (); i++)
351 Chord tremolo needs to beam over invisible stems of wholes
353 if (!dynamic_cast<Chord_tremolo*> (this))
355 if (s->invisible_b ())
359 Stem_info info (s, multiple_i_);
363 if (info.dir_ == dir_)
365 if (forced_count_i == total_count_i)
366 info.idealy_f_ -= shorten_f;
367 else if (forced_count_i > total_count_i / 2)
368 info.idealy_f_ -= shorten_f / 2;
375 Beam::calculate_slope ()
379 slope_f_ = left_y_ = 0;
380 else if (sinfo_[0].idealy_f_ == sinfo_.top ().idealy_f_)
383 left_y_ = sinfo_[0].idealy_f_;
389 Real solved_slope_f = slope_f_;
392 steep slope running against lengthened stem is suspect
394 Real dx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
396 // urg, these y internote-y-dimensions
397 Real internote_f = stems_[0]->staff_line_leading_f ()/2;
399 Real lengthened = paper_l ()->get_var ("beam_lengthened") / internote_f;
400 Real steep = paper_l ()->get_var ("beam_steep_slope") / internote_f;
401 if (((left_y_ - sinfo_[0].idealy_f_ > lengthened)
402 && (slope_f_ > steep))
403 || ((left_y_ + slope_f_ * dx_f - sinfo_.top ().idealy_f_ > lengthened)
404 && (slope_f_ < -steep)))
410 This neat trick is by Werner Lemberg,
411 damped = tanh (slope_f_)
412 corresponds with some tables in [Wanske]
414 SCM damp = remove_elt_property (damping_scm_sym);
415 int damping = 1; // ugh.
416 if (damp!= SCM_BOOL_F)
417 damping = gh_int2scm (SCM_CDR(damp));
420 slope_f_ = 0.6 * tanh (slope_f_) / damping;
424 Real damped_slope_dy_f = (solved_slope_f - slope_f_) * dx_f / 2;
425 left_y_ += damped_slope_dy_f;
436 [Ross] (simplification of)
437 Try to set slope_f_ complying with y-span of:
439 - beam_f / 2 + staffline_f / 2
440 - beam_f + staffline_f
444 if (quantisation_ <= NONE)
447 Real interline_f = stems_[0]->staff_line_leading_f ();
448 Real internote_f = interline_f / 2;
449 Real staffline_f = paper_l ()->rule_thickness ();
450 Real beam_f = paper_l ()->beam_thickness_f ();
452 Real dx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
454 // dim(y) = internote; so slope = (y/internote)/x
455 Real dy_f = dx_f * abs (slope_f_ * internote_f);
459 /* UGR. ICE in 2.8.1; bugreport filed. */
460 Array<Real> allowed_fraction (3);
461 allowed_fraction[0] = 0;
462 allowed_fraction[1] = (beam_f / 2 + staffline_f / 2);
463 allowed_fraction[2] = (beam_f + staffline_f);
466 Interval iv = quantise_iv (allowed_fraction, interline_f, dy_f);
467 quanty_f = (dy_f - iv[SMALLER] <= iv[BIGGER] - dy_f)
472 slope_f_ = (quanty_f / dx_f) / internote_f * sign (slope_f_);
475 static int test_pos = 0;
480 Prevent interference from stafflines and beams. See Documentation/tex/fonts.doc
484 Beam::quantise_left_y (bool extend_b)
487 we only need to quantise the start of the beam as dy is quantised too
488 if extend_b then stems must *not* get shorter
491 if (quantisation_ <= NONE)
495 ----------------------------------------------------------
499 --------------########------------------------------------
502 hang straddle sit inter hang
505 Real space = stems_[0]->staff_line_leading_f ();
506 Real internote_f = space /2;
507 Real staffline_f = paper_l ()->rule_thickness ();
508 Real beam_f = paper_l ()->beam_thickness_f ();
512 it would be nice to have all allowed positions in a runtime matrix:
513 (multiplicity, minimum_beam_dy, maximum_beam_dy)
517 Real sit = beam_f / 2 - staffline_f / 2;
518 Real inter = space / 2;
519 Real hang = space - beam_f / 2 + staffline_f / 2;
522 Put all allowed positions into an array.
523 Whether a position is allowed or not depends on
524 strictness of quantisation, multiplicity and direction.
526 For simplicity, we'll assume dir = UP and correct if
527 dir = DOWN afterwards.
530 // dim(left_y_) = internote
531 Real dy_f = dir_ * left_y_ * internote_f;
533 Real beamdx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
534 Real beamdy_f = beamdx_f * slope_f_ * internote_f;
536 Array<Real> allowed_position;
537 if (quantisation_ != TEST)
539 if (quantisation_ <= NORMAL)
541 if ((multiple_i_ <= 2) || (abs (beamdy_f) >= staffline_f / 2))
542 allowed_position.push (straddle);
543 if ((multiple_i_ <= 1) || (abs (beamdy_f) >= staffline_f / 2))
544 allowed_position.push (sit);
545 allowed_position.push (hang);
548 // TODO: check and fix TRADITIONAL
550 if ((multiple_i_ <= 2) || (abs (beamdy_f) >= staffline_f / 2))
551 allowed_position.push (straddle);
552 if ((multiple_i_ <= 1) && (beamdy_f <= staffline_f / 2))
553 allowed_position.push (sit);
554 if (beamdy_f >= -staffline_f / 2)
555 allowed_position.push (hang);
562 allowed_position.push (hang);
563 cout << "hang" << hang << "\n";
565 else if (test_pos==1)
567 allowed_position.push (straddle);
568 cout << "straddle" << straddle << endl;
570 else if (test_pos==2)
572 allowed_position.push (sit);
573 cout << "sit" << sit << endl;
575 else if (test_pos==3)
577 allowed_position.push (inter);
578 cout << "inter" << inter << endl;
582 Interval iv = quantise_iv (allowed_position, space, dy_f);
584 Real quanty_f = dy_f - iv[SMALLER] <= iv[BIGGER] - dy_f ? iv[SMALLER] : iv[BIGGER];
586 quanty_f = iv[BIGGER];
588 // dim(left_y_) = internote
589 left_y_ = dir_ * quanty_f / internote_f;
593 Beam::set_stemlens ()
595 Real staffline_f = paper_l ()->rule_thickness ();
597 Real epsilon_f = staffline_f / 8;
600 // je bent zelf eng --hwn.
601 Real dy_f = check_stemlengths_f (false);
602 for (int i = 0; i < 2; i++)
604 left_y_ += dy_f * dir_;
605 quantise_left_y (dy_f);
606 dy_f = check_stemlengths_f (true);
607 if (abs (dy_f) <= epsilon_f)
618 Beam::set_beaming (Beaming_info_list *beaming)
621 for (int i=0; i < stems_.size (); i++)
625 if (stems_[i]->beams_i_drul_[d] < 0)
626 stems_[i]->beams_i_drul_[d] = beaming->infos_.elem (i).beams_i_drul_[d];
628 while (flip (&d) != LEFT);
634 Beam::do_add_processing ()
636 for (int i=0; i < stems_.size () ; i++)
640 multiple_i_ = multiple_i_ >? stems_[i]->beams_i_drul_[d];
641 } while ((flip (&d)) != LEFT);
646 stems_[0]->beams_i_drul_[LEFT] =0;
647 stems_.top()->beams_i_drul_[RIGHT] =0;
654 beams to go with one stem.
657 Beam::stem_beams (Stem *here, Stem *next, Stem *prev) const
659 if ((next && !(next->hpos_f () > here->hpos_f ())) ||
660 (prev && !(prev->hpos_f () < here->hpos_f ())))
661 programming_error ("Beams are not left-to-right");
663 Real staffline_f = paper_l ()->rule_thickness ();
664 Real interbeam_f = paper_l ()->interbeam_f (multiple_i_);
666 Real internote_f = here->staff_line_leading_f ()/2;
667 Real beam_f = paper_l ()->beam_thickness_f ();
669 Real dy = interbeam_f;
670 Real stemdx = staffline_f;
671 Real sl = slope_f_* internote_f;
672 lookup_l ()->beam (sl, 20 PT, 1 PT);
679 if (!here->head_l_arr_.size ())
681 else if (here->type_i ()== 1)
682 nw_f = paper_l ()->get_var ("wholewidth");
683 else if (here->type_i () == 2)
684 nw_f = paper_l ()->note_width () * 0.8;
686 nw_f = paper_l ()->get_var ("quartwidth");
688 /* half beams extending to the left. */
691 int lhalfs= lhalfs = here->beams_i_drul_[LEFT] - prev->beams_i_drul_[RIGHT] ;
692 int lwholebeams= here->beams_i_drul_[LEFT] <? prev->beams_i_drul_[RIGHT] ;
694 Half beam should be one note-width,
695 but let's make sure two half-beams never touch
697 Real w = here->hpos_f () - prev->hpos_f ();
700 if (lhalfs) // generates warnings if not
701 a = lookup_l ()->beam (sl, w, beam_f);
702 a.translate (Offset (-w, -w * sl));
703 for (int j = 0; j < lhalfs; j++)
706 b.translate_axis (-dir_ * dy * (lwholebeams+j), Y_AXIS);
707 leftbeams.add_molecule (b);
713 int rhalfs = here->beams_i_drul_[RIGHT] - next->beams_i_drul_[LEFT];
714 int rwholebeams = here->beams_i_drul_[RIGHT] <? next->beams_i_drul_[LEFT];
716 Real w = next->hpos_f () - here->hpos_f ();
717 Molecule a = lookup_l ()->beam (sl, w + stemdx, beam_f);
718 a.translate_axis( - stemdx/2, X_AXIS);
722 SCM gap = get_elt_property (beam_gap_scm_sym);
723 if (gap != SCM_BOOL_F)
725 int gap_i = gh_scm2int (SCM_CDR (gap));
726 int nogap = rwholebeams - gap_i;
728 for (; j < nogap; j++)
731 b.translate_axis (-dir_ * dy * j, Y_AXIS);
732 rightbeams.add_molecule (b);
734 // TODO: notehead widths differ for different types
737 a = lookup_l ()->beam (sl, w + stemdx, beam_f);
740 for (; j < rwholebeams; j++)
743 if (!here->invisible_b ())
744 b.translate (Offset (gap_f, -dir_ * dy * j));
746 b.translate (Offset (0, -dir_ * dy * j));
747 rightbeams.add_molecule (b);
752 a = lookup_l ()->beam (sl, w, beam_f);
754 for (; j < rwholebeams + rhalfs; j++)
757 b.translate_axis (-dir_ * dy * j, Y_AXIS);
758 rightbeams.add_molecule (b);
762 leftbeams.add_molecule (rightbeams);
765 Does beam quanting think of the asymetry of beams?
766 Refpoint is on bottom of symbol. (FIXTHAT) --hwn.