2 beam.cc -- implement Beam
4 source file of the GNU LilyPond music typesetter
6 (c) 1997--1999, 1998 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.
28 #include "dimensions.hh"
32 #include "molecule.hh"
33 #include "leastsquares.hh"
35 #include "paper-def.hh"
37 #include "rhythmic-grouping.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;
174 for (int i=0; i <stems_.size (); i++)
177 int current = s->dir_
178 ? (1 + d * s->dir_)/2
179 : s->get_center_distance ((Direction)-d);
187 } while (flip(&d) != DOWN);
190 [Ross] states that the majority of the notes dictates the
191 direction (and not the mean of "center distance")
193 But is that because it really looks better, or because he
194 wants to provide some real simple hands-on rules.
196 We have our doubts, so we simply provide all sensible alternatives.
199 Dir_algorithm a = (Dir_algorithm)rint(paper_l ()->get_var ("beam_dir_algorithm"));
203 beamdir = (count[UP] > count[DOWN]) ? UP : DOWN;
206 // mean center distance
207 beamdir = (total[UP] > total[DOWN]) ? UP : DOWN;
211 // median center distance
214 else if (!count[DOWN])
217 beamdir = (total[UP] / count[UP] > total[DOWN] / count[DOWN]) ? UP : DOWN;
224 Beam::set_direction (Direction d)
227 for (int i=0; i <stems_.size (); i++)
230 s->set_elt_property (beam_dir_scm_sym, gh_int2scm (d));
232 SCM force = s->remove_elt_property (dir_forced_scm_sym);
233 if (force == SCM_BOOL_F)
239 See Documentation/tex/fonts.doc
245 assert (sinfo_.size () > 1);
246 DOUT << "Beam::solve_slope: \n";
249 for (int i=0; i < sinfo_.size (); i++)
251 l.input.push (Offset (sinfo_[i].x_, sinfo_[i].idealy_f_));
253 l.minimise (slope_f_, left_y_);
257 ugh. Naming: this doesn't check, but sets as well.
261 Beam::check_stemlengths_f (bool set_b)
263 Real interbeam_f = paper_l ()->interbeam_f (multiple_i_);
265 Real beam_f = paper_l ()->beam_thickness_f ();
266 Real staffline_f = paper_l ()->rule_thickness ();
267 Real epsilon_f = staffline_f / 8;
269 for (int i=0; i < sinfo_.size (); i++)
271 Real y = sinfo_[i].x_ * slope_f_ + left_y_;
274 if (dir_ != sinfo_[i].dir_)
276 Real internote_f = sinfo_[i].stem_l_->staff_line_leading_f ()/2;
277 y -= dir_ * (beam_f / 2
278 + (sinfo_[i].mult_i_ - 1) * interbeam_f) / internote_f;
279 if (!i && sinfo_[i].stem_l_->staff_symbol_l () !=
280 sinfo_.top ().stem_l_->staff_symbol_l ())
281 y += dir_ * (multiple_i_ - (sinfo_[i].stem_l_->flag_i_ - 2) >? 0)
282 * interbeam_f / internote_f;
286 sinfo_[i].stem_l_->set_stemend (y - sinfo_[i].interstaff_f_);
289 if (y > sinfo_[i].maxy_f_)
290 dy_f = dy_f <? sinfo_[i].maxy_f_ - y;
291 if (y < sinfo_[i].miny_f_)
293 // when all too short, normal stems win..
294 if (dy_f < -epsilon_f)
295 warning (_ ("weird beam shift, check your knees"));
296 dy_f = dy_f >? sinfo_[i].miny_f_ - y;
303 Beam::set_steminfo ()
308 assert (multiple_i_);
309 int total_count_i = 0;
310 int forced_count_i = 0;
311 for (int i=0; i < stems_.size (); i++)
315 s->set_default_extents ();
316 if (s->invisible_b ())
318 if (((int)s->chord_start_f ()) && (s->dir_ != s->get_default_dir ()))
323 int stem_max = (int)rint(paper_l ()->get_var ("stem_max"));
324 Real shorten_f = paper_l ()->get_var (String ("forced_stem_shorten"
325 + to_str (multiple_i_ <? stem_max)));
328 for (int i=0; i < stems_.size (); i++)
331 if (s->invisible_b ())
334 Stem_info info (s, multiple_i_);
338 if (info.dir_ == dir_)
340 if (forced_count_i == total_count_i)
341 info.idealy_f_ -= shorten_f;
342 else if (forced_count_i > total_count_i / 2)
343 info.idealy_f_ -= shorten_f / 2;
350 Beam::calculate_slope ()
354 slope_f_ = left_y_ = 0;
355 else if (sinfo_[0].idealy_f_ == sinfo_.top ().idealy_f_)
358 left_y_ = sinfo_[0].idealy_f_;
364 Real solved_slope_f = slope_f_;
367 steep slope running against lengthened stem is suspect
369 Real dx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
371 // urg, these y internote-y-dimensions
372 Real internote_f = stems_[0]->staff_line_leading_f ()/2;
374 Real lengthened = paper_l ()->get_var ("beam_lengthened") / internote_f;
375 Real steep = paper_l ()->get_var ("beam_steep_slope") / internote_f;
376 if (((left_y_ - sinfo_[0].idealy_f_ > lengthened)
377 && (slope_f_ > steep))
378 || ((left_y_ + slope_f_ * dx_f - sinfo_.top ().idealy_f_ > lengthened)
379 && (slope_f_ < -steep)))
385 This neat trick is by Werner Lemberg,
386 damped = tanh (slope_f_)
387 corresponds with some tables in [Wanske]
389 SCM damp = remove_elt_property (damping_scm_sym);
390 int damping = 1; // ugh.
391 if (damp!= SCM_BOOL_F)
392 damping = gh_int2scm (SCM_CDR(damp));
395 slope_f_ = 0.6 * tanh (slope_f_) / damping;
399 Real damped_slope_dy_f = (solved_slope_f - slope_f_) * dx_f / 2;
400 left_y_ += damped_slope_dy_f;
411 [Ross] (simplification of)
412 Try to set slope_f_ complying with y-span of:
414 - beam_f / 2 + staffline_f / 2
415 - beam_f + staffline_f
419 if (quantisation_ <= NONE)
422 Real interline_f = stems_[0]->staff_line_leading_f ();
423 Real internote_f = interline_f / 2;
424 Real staffline_f = paper_l ()->rule_thickness ();
425 Real beam_f = paper_l ()->beam_thickness_f ();
427 Real dx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
429 // dim(y) = internote; so slope = (y/internote)/x
430 Real dy_f = dx_f * abs (slope_f_ * internote_f);
434 /* UGR. ICE in 2.8.1; bugreport filed. */
435 Array<Real> allowed_fraction (3);
436 allowed_fraction[0] = 0;
437 allowed_fraction[1] = (beam_f / 2 + staffline_f / 2);
438 allowed_fraction[2] = (beam_f + staffline_f);
441 Interval iv = quantise_iv (allowed_fraction, interline_f, dy_f);
442 quanty_f = (dy_f - iv[SMALLER] <= iv[BIGGER] - dy_f)
447 slope_f_ = (quanty_f / dx_f) / internote_f * sign (slope_f_);
450 static int test_pos = 0;
455 Prevent interference from stafflines and beams. See Documentation/tex/fonts.doc
459 Beam::quantise_left_y (bool extend_b)
462 we only need to quantise the start of the beam as dy is quantised too
463 if extend_b then stems must *not* get shorter
466 if (quantisation_ <= NONE)
470 ----------------------------------------------------------
474 --------------########------------------------------------
477 hang straddle sit inter hang
480 Real space = stems_[0]->staff_line_leading_f ();
481 Real internote_f = space /2;
482 Real staffline_f = paper_l ()->rule_thickness ();
483 Real beam_f = paper_l ()->beam_thickness_f ();
487 it would be nice to have all allowed positions in a runtime matrix:
488 (multiplicity, minimum_beam_dy, maximum_beam_dy)
492 Real sit = beam_f / 2 - staffline_f / 2;
493 Real inter = space / 2;
494 Real hang = space - beam_f / 2 + staffline_f / 2;
497 Put all allowed positions into an array.
498 Whether a position is allowed or not depends on
499 strictness of quantisation, multiplicity and direction.
501 For simplicity, we'll assume dir = UP and correct if
502 dir = DOWN afterwards.
505 // dim(left_y_) = internote
506 Real dy_f = dir_ * left_y_ * internote_f;
508 Real beamdx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
509 Real beamdy_f = beamdx_f * slope_f_ * internote_f;
511 Array<Real> allowed_position;
512 if (quantisation_ != TEST)
514 if (quantisation_ <= NORMAL)
516 if ((multiple_i_ <= 2) || (abs (beamdy_f) >= staffline_f / 2))
517 allowed_position.push (straddle);
518 if ((multiple_i_ <= 1) || (abs (beamdy_f) >= staffline_f / 2))
519 allowed_position.push (sit);
520 allowed_position.push (hang);
523 // TODO: check and fix TRADITIONAL
525 if ((multiple_i_ <= 2) || (abs (beamdy_f) >= staffline_f / 2))
526 allowed_position.push (straddle);
527 if ((multiple_i_ <= 1) && (beamdy_f <= staffline_f / 2))
528 allowed_position.push (sit);
529 if (beamdy_f >= -staffline_f / 2)
530 allowed_position.push (hang);
537 allowed_position.push (hang);
538 cout << "hang" << hang << "\n";
540 else if (test_pos==1)
542 allowed_position.push (straddle);
543 cout << "straddle" << straddle << endl;
545 else if (test_pos==2)
547 allowed_position.push (sit);
548 cout << "sit" << sit << endl;
550 else if (test_pos==3)
552 allowed_position.push (inter);
553 cout << "inter" << inter << endl;
557 Interval iv = quantise_iv (allowed_position, space, dy_f);
559 Real quanty_f = dy_f - iv[SMALLER] <= iv[BIGGER] - dy_f ? iv[SMALLER] : iv[BIGGER];
561 quanty_f = iv[BIGGER];
563 // dim(left_y_) = internote
564 left_y_ = dir_ * quanty_f / internote_f;
568 Beam::set_stemlens ()
570 Real staffline_f = paper_l ()->rule_thickness ();
572 Real epsilon_f = staffline_f / 8;
574 Real dy_f = check_stemlengths_f (false);
575 for (int i = 0; i < 2; i++)
577 left_y_ += dy_f * dir_;
578 quantise_left_y (dy_f);
579 dy_f = check_stemlengths_f (true);
580 if (abs (dy_f) <= epsilon_f)
592 ugh. this is broken and should be rewritten.
596 Beam::set_grouping (Rhythmic_grouping def, Rhythmic_grouping cur)
600 assert (cur.children.size () == stems_.size ());
607 for (int j=0; j <stems_.size (); j++)
611 int f = s->flag_i_ - 2;
616 b= cur.generate_beams (flags, fi);
619 assert (stems_.size () == b.size ()/2);
622 for (int j=0, i=0; i < b.size () && j <stems_.size (); j++)
627 if (s->beams_i_drul_[d] < 0)
628 s->beams_i_drul_[d] = b[i];
630 multiple_i_ = multiple_i_ >? s->beams_i_drul_[d];
632 } while ((flip (&d)) != LEFT);
637 beams to go with one stem.
640 Beam::stem_beams (Stem *here, Stem *next, Stem *prev) const
642 assert (!next || next->hpos_f () > here->hpos_f ());
643 assert (!prev || prev->hpos_f () < here->hpos_f ());
645 Real staffline_f = paper_l ()->rule_thickness ();
646 Real interbeam_f = paper_l ()->interbeam_f (multiple_i_);
648 Real internote_f = here->staff_line_leading_f ()/2;
649 Real beam_f = paper_l ()->beam_thickness_f ();
651 Real dy = interbeam_f;
652 Real stemdx = staffline_f;
653 Real sl = slope_f_* internote_f;
654 lookup_l ()->beam (sl, 20 PT, 1 PT);
660 Real nw_f = paper_l ()->note_width () * 0.8;
662 /* half beams extending to the left. */
665 int lhalfs= lhalfs = here->beams_i_drul_[LEFT] - prev->beams_i_drul_[RIGHT] ;
666 int lwholebeams= here->beams_i_drul_[LEFT] <? prev->beams_i_drul_[RIGHT] ;
668 Half beam should be one note-width,
669 but let's make sure two half-beams never touch
671 Real w = here->hpos_f () - prev->hpos_f ();
674 if (lhalfs) // generates warnings if not
675 a = lookup_l ()->beam (sl, w, beam_f);
676 a.translate (Offset (-w, -w * sl));
677 for (int j = 0; j < lhalfs; j++)
680 b.translate_axis (-dir_ * dy * (lwholebeams+j), Y_AXIS);
681 leftbeams.add_molecule (b);
687 int rhalfs = here->beams_i_drul_[RIGHT] - next->beams_i_drul_[LEFT];
688 int rwholebeams = here->beams_i_drul_[RIGHT] <? next->beams_i_drul_[LEFT];
690 Real w = next->hpos_f () - here->hpos_f ();
691 Molecule a = lookup_l ()->beam (sl, w + stemdx, beam_f);
692 a.translate_axis( - stemdx/2, X_AXIS);
696 SCM gap = get_elt_property (beam_gap_scm_sym);
697 if (gap != SCM_BOOL_F)
699 int gap_i = gh_scm2int (gap);
700 int nogap = rwholebeams - gap_i;
702 for (; j < nogap; j++)
705 b.translate_axis (-dir_ * dy * j, Y_AXIS);
706 rightbeams.add_molecule (b);
708 // TODO: notehead widths differ for different types
711 a = lookup_l ()->beam (sl, w + stemdx, beam_f);
714 for (; j < rwholebeams; j++)
717 b.translate (Offset (gap_f, -dir_ * dy * j));
718 rightbeams.add_molecule (b);
723 a = lookup_l ()->beam (sl, w, beam_f);
725 for (; j < rwholebeams + rhalfs; j++)
728 b.translate_axis (-dir_ * dy * j, Y_AXIS);
729 rightbeams.add_molecule (b);
733 leftbeams.add_molecule (rightbeams);
736 Does beam quanting think of the asymetry of beams?
737 Refpoint is on bottom of symbol. (FIXTHAT) --hwn.