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 "new-beaming.hh"
29 #include "dimensions.hh"
33 #include "molecule.hh"
34 #include "leastsquares.hh"
36 #include "paper-def.hh"
43 quantisation_ = NORMAL;
48 Beam::add_stem (Stem*s)
53 dim_cache_[Y_AXIS]->parent_l_ = s->dim_cache_[Y_AXIS];
57 s->add_dependency (this);
62 if (!spanned_drul_[LEFT])
69 Beam::get_stem_info (Stem *s)
72 for (int i=0; i < sinfo_.size (); i++)
74 if (sinfo_[i].stem_l_ == s)
82 Beam::do_brew_molecule_p () const
84 Molecule *mol_p = new Molecule;
88 Real x0 = stems_[0]->hpos_f ();
89 for (int j=0; j <stems_.size (); j++)
92 Stem * prev = (j > 0)? stems_[j-1] : 0;
93 Stem * next = (j < stems_.size ()-1) ? stems_[j+1] :0;
95 Molecule sb = stem_beams (i, next, prev);
96 Real x = i->hpos_f ()-x0;
97 sb.translate (Offset (x, (x * slope_f_ + left_y_) *
98 i->staff_line_leading_f ()/2 ));
99 mol_p->add_molecule (sb);
101 mol_p->translate_axis (x0
102 - spanned_drul_[LEFT]->absolute_coordinate (X_AXIS), X_AXIS);
108 Beam::center () const
110 Stem_info si = sinfo_[0];
112 Real w= (si.stem_l_->note_delta_f () + extent (X_AXIS).length ())/2.0;
113 return Offset (w, ( w* slope_f_) *
114 si.stem_l_->staff_line_leading_f ()/2);
118 Beam::do_pre_processing ()
121 dir_ = get_default_dir ();
124 set_direction (dir_);
128 Beam::do_print () const
131 DOUT << "slope_f_ " << slope_f_ << "left ypos " << left_y_;
132 Spanner::do_print ();
137 Beam::do_post_processing ()
139 if (stems_.size () < 2)
141 warning (_ ("beam with less than two stems"));
142 set_elt_property (transparent_scm_sym, SCM_BOOL_T);
150 Beam::do_substitute_element_pointer (Score_element*o,Score_element*n)
152 if (Stem * os = dynamic_cast<Stem*> (o))
153 stems_.substitute (os,
154 dynamic_cast<Stem *> (n));
158 Beam::do_width () const
160 return Interval (stems_[0]->hpos_f (),
161 stems_.top ()->hpos_f ());
165 Beam::get_default_dir () const
167 Drul_array<int> total;
168 total[UP] = total[DOWN] = 0;
169 Drul_array<int> count;
170 count[UP] = count[DOWN] = 0;
173 for (int i=0; i <stems_.size (); i++)
176 int current = s->dir_
177 ? (1 + d * s->dir_)/2
178 : s->get_center_distance ((Direction)-d);
186 } while (flip(&d) != DOWN);
189 [Ross] states that the majority of the notes dictates the
190 direction (and not the mean of "center distance")
192 But is that because it really looks better, or because he
193 wants to provide some real simple hands-on rules.
195 We have our doubts, so we simply provide all sensible alternatives.
197 If dir is not determined: up (see stem::get_default_dir ())
201 Direction neutral_dir = (int)paper_l ()->get_var ("stem_default_neutral_direction");
203 Dir_algorithm a = (Dir_algorithm)rint(paper_l ()->get_var ("beam_dir_algorithm"));
207 beam_dir = (count[UP] == count[DOWN]) ? neutral_dir
208 : (count[UP] > count[DOWN]) ? UP : DOWN;
211 // mean center distance
212 beam_dir = (total[UP] == total[DOWN]) ? neutral_dir
213 : (total[UP] > total[DOWN]) ? UP : DOWN;
217 // median center distance
218 if (!count[DOWN] || !count[UP])
220 beam_dir = (count[UP] == count[DOWN]) ? neutral_dir
221 : (count[UP] > count[DOWN]) ? UP : DOWN;
225 beam_dir = (total[UP] / count[UP] == total[DOWN] / count[DOWN])
227 : (total[UP] / count[UP] > total[DOWN] / count[DOWN]) ? UP : DOWN;
235 Beam::set_direction (Direction d)
238 for (int i=0; i <stems_.size (); i++)
241 s->set_elt_property (beam_dir_scm_sym, gh_int2scm (d));
243 SCM force = s->remove_elt_property (dir_forced_scm_sym);
244 if (force == SCM_BOOL_F)
250 See Documentation/tex/fonts.doc
256 assert (sinfo_.size () > 1);
257 DOUT << "Beam::solve_slope: \n";
260 for (int i=0; i < sinfo_.size (); i++)
262 l.input.push (Offset (sinfo_[i].x_, sinfo_[i].idealy_f_));
264 l.minimise (slope_f_, left_y_);
268 ugh. Naming: this doesn't check, but sets as well.
272 Beam::check_stemlengths_f (bool set_b)
274 Real interbeam_f = paper_l ()->interbeam_f (multiple_i_);
276 Real beam_f = paper_l ()->beam_thickness_f ();
277 Real staffline_f = paper_l ()->rule_thickness ();
278 Real epsilon_f = staffline_f / 8;
280 for (int i=0; i < sinfo_.size (); i++)
282 Real y = sinfo_[i].x_ * slope_f_ + left_y_;
285 if (dir_ != sinfo_[i].dir_)
287 Real internote_f = sinfo_[i].stem_l_->staff_line_leading_f ()/2;
288 y -= dir_ * (beam_f / 2
289 + (sinfo_[i].mult_i_ - 1) * interbeam_f) / internote_f;
290 if (!i && sinfo_[i].stem_l_->staff_symbol_l () !=
291 sinfo_.top ().stem_l_->staff_symbol_l ())
292 y += dir_ * (multiple_i_ - (sinfo_[i].stem_l_->flag_i_ - 2) >? 0)
293 * interbeam_f / internote_f;
297 sinfo_[i].stem_l_->set_stemend (y - sinfo_[i].interstaff_f_);
300 if (y > sinfo_[i].maxy_f_)
301 dy_f = dy_f <? sinfo_[i].maxy_f_ - y;
302 if (y < sinfo_[i].miny_f_)
304 // when all too short, normal stems win..
305 if (dy_f < -epsilon_f)
306 warning (_ ("weird beam shift, check your knees"));
307 dy_f = dy_f >? sinfo_[i].miny_f_ - y;
314 Beam::set_steminfo ()
319 assert (multiple_i_);
320 int total_count_i = 0;
321 int forced_count_i = 0;
322 for (int i=0; i < stems_.size (); i++)
326 s->set_default_extents ();
327 if (s->invisible_b ())
329 if (((int)s->chord_start_f ()) && (s->dir_ != s->get_default_dir ()))
334 bool grace_b = get_elt_property (grace_scm_sym) != SCM_BOOL_F;
335 String type_str = grace_b ? "grace_" : "";
336 int stem_max = (int)rint(paper_l ()->get_var ("stem_max"));
337 Real shorten_f = paper_l ()->get_var (type_str + "forced_stem_shorten"
338 + to_str (multiple_i_ <? stem_max));
341 for (int i=0; i < stems_.size (); i++)
344 if (s->invisible_b ())
347 Stem_info info (s, multiple_i_);
351 if (info.dir_ == dir_)
353 if (forced_count_i == total_count_i)
354 info.idealy_f_ -= shorten_f;
355 else if (forced_count_i > total_count_i / 2)
356 info.idealy_f_ -= shorten_f / 2;
363 Beam::calculate_slope ()
367 slope_f_ = left_y_ = 0;
368 else if (sinfo_[0].idealy_f_ == sinfo_.top ().idealy_f_)
371 left_y_ = sinfo_[0].idealy_f_;
377 Real solved_slope_f = slope_f_;
380 steep slope running against lengthened stem is suspect
382 Real dx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
384 // urg, these y internote-y-dimensions
385 Real internote_f = stems_[0]->staff_line_leading_f ()/2;
387 Real lengthened = paper_l ()->get_var ("beam_lengthened") / internote_f;
388 Real steep = paper_l ()->get_var ("beam_steep_slope") / internote_f;
389 if (((left_y_ - sinfo_[0].idealy_f_ > lengthened)
390 && (slope_f_ > steep))
391 || ((left_y_ + slope_f_ * dx_f - sinfo_.top ().idealy_f_ > lengthened)
392 && (slope_f_ < -steep)))
398 This neat trick is by Werner Lemberg,
399 damped = tanh (slope_f_)
400 corresponds with some tables in [Wanske]
402 SCM damp = remove_elt_property (damping_scm_sym);
403 int damping = 1; // ugh.
404 if (damp!= SCM_BOOL_F)
405 damping = gh_int2scm (SCM_CDR(damp));
408 slope_f_ = 0.6 * tanh (slope_f_) / damping;
412 Real damped_slope_dy_f = (solved_slope_f - slope_f_) * dx_f / 2;
413 left_y_ += damped_slope_dy_f;
424 [Ross] (simplification of)
425 Try to set slope_f_ complying with y-span of:
427 - beam_f / 2 + staffline_f / 2
428 - beam_f + staffline_f
432 if (quantisation_ <= NONE)
435 Real interline_f = stems_[0]->staff_line_leading_f ();
436 Real internote_f = interline_f / 2;
437 Real staffline_f = paper_l ()->rule_thickness ();
438 Real beam_f = paper_l ()->beam_thickness_f ();
440 Real dx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
442 // dim(y) = internote; so slope = (y/internote)/x
443 Real dy_f = dx_f * abs (slope_f_ * internote_f);
447 /* UGR. ICE in 2.8.1; bugreport filed. */
448 Array<Real> allowed_fraction (3);
449 allowed_fraction[0] = 0;
450 allowed_fraction[1] = (beam_f / 2 + staffline_f / 2);
451 allowed_fraction[2] = (beam_f + staffline_f);
454 Interval iv = quantise_iv (allowed_fraction, interline_f, dy_f);
455 quanty_f = (dy_f - iv[SMALLER] <= iv[BIGGER] - dy_f)
460 slope_f_ = (quanty_f / dx_f) / internote_f * sign (slope_f_);
463 static int test_pos = 0;
468 Prevent interference from stafflines and beams. See Documentation/tex/fonts.doc
472 Beam::quantise_left_y (bool extend_b)
475 we only need to quantise the start of the beam as dy is quantised too
476 if extend_b then stems must *not* get shorter
479 if (quantisation_ <= NONE)
483 ----------------------------------------------------------
487 --------------########------------------------------------
490 hang straddle sit inter hang
493 Real space = stems_[0]->staff_line_leading_f ();
494 Real internote_f = space /2;
495 Real staffline_f = paper_l ()->rule_thickness ();
496 Real beam_f = paper_l ()->beam_thickness_f ();
500 it would be nice to have all allowed positions in a runtime matrix:
501 (multiplicity, minimum_beam_dy, maximum_beam_dy)
505 Real sit = beam_f / 2 - staffline_f / 2;
506 Real inter = space / 2;
507 Real hang = space - beam_f / 2 + staffline_f / 2;
510 Put all allowed positions into an array.
511 Whether a position is allowed or not depends on
512 strictness of quantisation, multiplicity and direction.
514 For simplicity, we'll assume dir = UP and correct if
515 dir = DOWN afterwards.
518 // dim(left_y_) = internote
519 Real dy_f = dir_ * left_y_ * internote_f;
521 Real beamdx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
522 Real beamdy_f = beamdx_f * slope_f_ * internote_f;
524 Array<Real> allowed_position;
525 if (quantisation_ != TEST)
527 if (quantisation_ <= NORMAL)
529 if ((multiple_i_ <= 2) || (abs (beamdy_f) >= staffline_f / 2))
530 allowed_position.push (straddle);
531 if ((multiple_i_ <= 1) || (abs (beamdy_f) >= staffline_f / 2))
532 allowed_position.push (sit);
533 allowed_position.push (hang);
536 // TODO: check and fix TRADITIONAL
538 if ((multiple_i_ <= 2) || (abs (beamdy_f) >= staffline_f / 2))
539 allowed_position.push (straddle);
540 if ((multiple_i_ <= 1) && (beamdy_f <= staffline_f / 2))
541 allowed_position.push (sit);
542 if (beamdy_f >= -staffline_f / 2)
543 allowed_position.push (hang);
550 allowed_position.push (hang);
551 cout << "hang" << hang << "\n";
553 else if (test_pos==1)
555 allowed_position.push (straddle);
556 cout << "straddle" << straddle << endl;
558 else if (test_pos==2)
560 allowed_position.push (sit);
561 cout << "sit" << sit << endl;
563 else if (test_pos==3)
565 allowed_position.push (inter);
566 cout << "inter" << inter << endl;
570 Interval iv = quantise_iv (allowed_position, space, dy_f);
572 Real quanty_f = dy_f - iv[SMALLER] <= iv[BIGGER] - dy_f ? iv[SMALLER] : iv[BIGGER];
574 quanty_f = iv[BIGGER];
576 // dim(left_y_) = internote
577 left_y_ = dir_ * quanty_f / internote_f;
581 Beam::set_stemlens ()
583 Real staffline_f = paper_l ()->rule_thickness ();
585 Real epsilon_f = staffline_f / 8;
588 // je bent zelf eng --hwn.
589 Real dy_f = check_stemlengths_f (false);
590 for (int i = 0; i < 2; i++)
592 left_y_ += dy_f * dir_;
593 quantise_left_y (dy_f);
594 dy_f = check_stemlengths_f (true);
595 if (abs (dy_f) <= epsilon_f)
606 Beam::set_beaming (Beaming_info_list *beaming)
609 for (int i=0; i < stems_.size (); i++)
613 if (stems_[i]->beams_i_drul_[d] < 0)
614 stems_[i]->beams_i_drul_[d] = beaming->infos_.elem (i).beams_i_drul_[d];
616 while (flip (&d) != LEFT);
622 Beam::do_add_processing ()
624 for (int i=0; i < stems_.size () ; i++)
628 multiple_i_ = multiple_i_ >? stems_[i]->beams_i_drul_[d];
629 } while ((flip (&d)) != LEFT);
634 stems_[0]->beams_i_drul_[LEFT] =0;
635 stems_.top()->beams_i_drul_[RIGHT] =0;
642 beams to go with one stem.
645 Beam::stem_beams (Stem *here, Stem *next, Stem *prev) const
647 if ((next && !(next->hpos_f () > here->hpos_f ())) ||
648 (prev && !(prev->hpos_f () < here->hpos_f ())))
649 programming_error ("Beams are not left-to-right");
651 Real staffline_f = paper_l ()->rule_thickness ();
652 Real interbeam_f = paper_l ()->interbeam_f (multiple_i_);
654 Real internote_f = here->staff_line_leading_f ()/2;
655 Real beam_f = paper_l ()->beam_thickness_f ();
657 Real dy = interbeam_f;
658 Real stemdx = staffline_f;
659 Real sl = slope_f_* internote_f;
660 lookup_l ()->beam (sl, 20 PT, 1 PT);
666 Real nw_f = paper_l ()->note_width () * 0.8;
668 /* half beams extending to the left. */
671 int lhalfs= lhalfs = here->beams_i_drul_[LEFT] - prev->beams_i_drul_[RIGHT] ;
672 int lwholebeams= here->beams_i_drul_[LEFT] <? prev->beams_i_drul_[RIGHT] ;
674 Half beam should be one note-width,
675 but let's make sure two half-beams never touch
677 Real w = here->hpos_f () - prev->hpos_f ();
680 if (lhalfs) // generates warnings if not
681 a = lookup_l ()->beam (sl, w, beam_f);
682 a.translate (Offset (-w, -w * sl));
683 for (int j = 0; j < lhalfs; j++)
686 b.translate_axis (-dir_ * dy * (lwholebeams+j), Y_AXIS);
687 leftbeams.add_molecule (b);
693 int rhalfs = here->beams_i_drul_[RIGHT] - next->beams_i_drul_[LEFT];
694 int rwholebeams = here->beams_i_drul_[RIGHT] <? next->beams_i_drul_[LEFT];
696 Real w = next->hpos_f () - here->hpos_f ();
697 Molecule a = lookup_l ()->beam (sl, w + stemdx, beam_f);
698 a.translate_axis( - stemdx/2, X_AXIS);
702 SCM gap = get_elt_property (beam_gap_scm_sym);
703 if (gap != SCM_BOOL_F)
705 int gap_i = gh_scm2int (gap);
706 int nogap = rwholebeams - gap_i;
708 for (; j < nogap; j++)
711 b.translate_axis (-dir_ * dy * j, Y_AXIS);
712 rightbeams.add_molecule (b);
714 // TODO: notehead widths differ for different types
717 a = lookup_l ()->beam (sl, w + stemdx, beam_f);
720 for (; j < rwholebeams; j++)
723 b.translate (Offset (gap_f, -dir_ * dy * j));
724 rightbeams.add_molecule (b);
729 a = lookup_l ()->beam (sl, w, beam_f);
731 for (; j < rwholebeams + rhalfs; j++)
734 b.translate_axis (-dir_ * dy * j, Y_AXIS);
735 rightbeams.add_molecule (b);
739 leftbeams.add_molecule (rightbeams);
742 Does beam quanting think of the asymetry of beams?
743 Refpoint is on bottom of symbol. (FIXTHAT) --hwn.