2 beam.cc -- implement Beam
4 source file of the GNU LilyPond music typesetter
6 (c) 1997--1998, 1998 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7 Jan Nieuwenhuizen <janneke@gnu.org>
24 #include "dimensions.hh"
26 #include "abbreviation-beam.hh"
30 #include "molecule.hh"
31 #include "leastsquares.hh"
33 #include "paper-def.hh"
35 #include "rhythmic-grouping.hh"
42 quantisation_ = NORMAL;
44 vertical_align_drul_[MIN] = 0;
45 vertical_align_drul_[MAX] = -1;
49 Beam::add_stem (Stem*s)
52 s->add_dependency (this);
55 if (!spanned_drul_[LEFT])
62 Beam::do_brew_molecule_p () const
64 Molecule *mol_p = new Molecule;
65 Real internote_f = paper ()->internote_f ();
67 Real x0 = stems_[0]->hpos_f ();
68 for (int j=0; j <stems_.size (); j++)
71 Stem * prev = (j > 0)? stems_[j-1] : 0;
72 Stem * next = (j < stems_.size ()-1) ? stems_[j+1] :0;
74 Molecule sb = stem_beams (i, next, prev);
75 Real x = i->hpos_f ()-x0;
76 sb.translate (Offset (x, (x * slope_f_ + left_y_) * internote_f));
77 mol_p->add_molecule (sb);
79 mol_p->translate_axis (x0
80 - spanned_drul_[LEFT]->absolute_coordinate (X_AXIS), X_AXIS);
82 // correct if last note (and therefore reference point of beam)
83 // is on different staff
84 mol_p->translate_axis (- sinfo_.top ().interstaff_f_ * internote_f, Y_AXIS);
92 Real w= (paper ()->note_width () + extent (X_AXIS).length ())/2.0;
93 return Offset (w, (left_y_ + w* slope_f_)*paper ()->internote_f ());
97 Beam::do_pre_processing ()
104 Beam::do_print () const
107 DOUT << "slope_f_ " << slope_f_ << "left ypos " << left_y_;
108 Spanner::do_print ();
113 Beam::do_post_processing ()
115 if (stems_.size () < 2)
117 warning (_ ("beam with less than two stems"));
118 transparent_b_ = true;
126 Beam::do_substitute_dependent (Score_element*o,Score_element*n)
128 if (Stem * os = dynamic_cast<Stem*> (o))
129 stems_.substitute (os,
130 dynamic_cast<Stem *> (n));
134 Beam::do_width () const
136 return Interval (stems_[0]->hpos_f (),
137 stems_.top ()->hpos_f ());
141 Beam::set_default_dir ()
143 Drul_array<int> total;
144 total[UP] = total[DOWN] = 0;
145 Drul_array<int> count;
146 count[UP] = count[DOWN] = 0;
149 for (int i=0; i <stems_.size (); i++)
152 int current = s->dir_
153 ? (1 + d * s->dir_)/2
154 : s->get_center_distance ((Direction)-d);
162 } while (flip(&d) != DOWN);
165 [Ross] states that the majority of the notes dictates the
166 direction (and not the mean of "center distance")
168 But is that because it really looks better, or because he
169 wants to provide some real simple hands-on rules.
171 We have our doubts, so we simply provide all sensible alternatives.
174 Dir_algorithm a = (Dir_algorithm)rint(paper ()->get_var ("beam_dir_algorithm"));
178 dir_ = (count[UP] > count[DOWN]) ? UP : DOWN;
181 // mean centre distance
182 dir_ = (total[UP] > total[DOWN]) ? UP : DOWN;
186 // median centre distance
189 else if (!count[DOWN])
192 dir_ = (total[UP] / count[UP] > total[DOWN] / count[DOWN]) ? UP : DOWN;
196 for (int i=0; i <stems_.size (); i++)
200 if (!s->dir_forced_b_)
206 See Documentation/tex/fonts.doc
213 should use minimum energy formulation (cf linespacing)
215 assert (sinfo_.size () > 1);
216 DOUT << "Beam::solve_slope: \n";
219 for (int i=0; i < sinfo_.size (); i++)
221 l.input.push (Offset (sinfo_[i].x_, sinfo_[i].idealy_f_));
223 l.minimise (slope_f_, left_y_);
227 Beam::check_stemlengths_f (bool set_b)
229 Real interbeam_f = paper ()->interbeam_f (multiple_i_);
230 Real internote_f = paper ()->internote_f ();
231 Real beam_f = paper ()->beam_thickness_f ();
232 Real staffline_f = paper ()->rule_thickness ();
233 Real epsilon_f = staffline_f / 8;
235 for (int i=0; i < sinfo_.size (); i++)
237 Real y = sinfo_[i].x_ * slope_f_ + left_y_;
240 if (dir_ != sinfo_[i].dir_)
242 y -= dir_ * (beam_f / 2
243 + (sinfo_[i].mult_i_ - 1) * interbeam_f) / internote_f;
244 if (!i && sinfo_[i].stem_l_->staff_sym_l_ !=
245 sinfo_.top ().stem_l_->staff_sym_l_)
246 y += dir_ * (multiple_i_ - (sinfo_[i].stem_l_->flag_i_ - 2) >? 0)
247 * interbeam_f / internote_f;
251 sinfo_[i].stem_l_->set_stemend (y - sinfo_[i].interstaff_f_);
254 if (y > sinfo_[i].maxy_f_)
255 dy_f = dy_f <? sinfo_[i].maxy_f_ - y;
256 if (y < sinfo_[i].miny_f_)
258 // when all too short, normal stems win..
259 if (dy_f < -epsilon_f)
260 warning (_ ("weird beam shift, check your knees"));
261 dy_f = dy_f >? sinfo_[i].miny_f_ - y;
268 Beam::set_steminfo ()
270 assert (multiple_i_);
271 int total_count_i = 0;
272 int forced_count_i = 0;
273 for (int i=0; i < stems_.size (); i++)
276 s->mult_i_ = multiple_i_;
277 s->set_default_extents ();
278 if (s->invisible_b ())
280 if (((int)s->chord_start_f ()) && (s->dir_ != s->get_default_dir ()))
285 Real internote_f = paper ()->internote_f ();
286 int stem_max = (int)rint(paper ()->get_var ("stem_max"));
287 Real shorten_f = paper ()->get_var (String ("forced_stem_shorten"
288 + to_str (multiple_i_ <? stem_max)))
292 for (int i=0; i < stems_.size (); i++)
295 if (s->invisible_b ())
302 if (info.dir_ == dir_)
304 if (forced_count_i == total_count_i)
305 info.idealy_f_ -= shorten_f;
306 else if (forced_count_i > total_count_i / 2)
307 info.idealy_f_ -= shorten_f / 2;
314 Beam::calculate_slope ()
318 slope_f_ = left_y_ = 0;
319 else if (sinfo_[0].idealy_f_ == sinfo_.top ().idealy_f_)
322 left_y_ = sinfo_[0].idealy_f_;
328 Real solved_slope_f = slope_f_;
331 steep slope running against lengthened stem is suspect
333 Real dx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
335 // urg, these y internote-y-dimensions
336 Real internote_f = paper ()->internote_f ();
337 Real lengthened = paper ()->get_var ("beam_lengthened") / internote_f;
338 Real steep = paper ()->get_var ("beam_steep_slope") / internote_f;
339 if (((left_y_ - sinfo_[0].idealy_f_ > lengthened)
340 && (slope_f_ > steep))
341 || ((left_y_ + slope_f_ * dx_f - sinfo_.top ().idealy_f_ > lengthened)
342 && (slope_f_ < -steep)))
348 This neat trick is by Werner Lemberg,
349 damped = tanh (slope_f_)
350 corresponds with some tables in [Wanske]
353 slope_f_ = 0.6 * tanh (slope_f_) / damping_i_;
357 Real damped_slope_dy_f = (solved_slope_f - slope_f_) * dx_f / 2;
358 left_y_ += damped_slope_dy_f;
369 [Ross] (simplification of)
370 Try to set slope_f_ complying with y-span of:
372 - beam_f / 2 + staffline_f / 2
373 - beam_f + staffline_f
377 if (quantisation_ <= NONE)
380 Real interline_f = paper ()->interline_f ();
381 Real internote_f = interline_f / 2;
382 Real staffline_f = paper ()->rule_thickness ();
383 Real beam_f = paper ()->beam_thickness_f ();
385 Real dx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
387 // dim(y) = internote; so slope = (y/internote)/x
388 Real dy_f = dx_f * abs (slope_f_ * internote_f);
392 /* UGR. ICE in 2.8.1; bugreport filed. */
393 Array<Real> allowed_fraction (3);
394 allowed_fraction[0] = 0;
395 allowed_fraction[1] = (beam_f / 2 + staffline_f / 2);
396 allowed_fraction[2] = (beam_f + staffline_f);
399 Interval iv = quantise_iv (allowed_fraction, interline_f, dy_f);
400 quanty_f = (dy_f - iv.min () <= iv.max () - dy_f)
405 slope_f_ = (quanty_f / dx_f) / internote_f * sign (slope_f_);
408 static int test_pos = 0;
413 Prevent interference from stafflines and beams. See Documentation/tex/fonts.doc
417 Beam::quantise_left_y (bool extend_b)
420 we only need to quantise the start of the beam as dy is quantised too
421 if extend_b then stems must *not* get shorter
424 if (quantisation_ <= NONE)
428 ----------------------------------------------------------
432 --------------########------------------------------------
435 hang straddle sit inter hang
438 Real interline_f = paper ()->interline_f ();
439 Real internote_f = paper ()->internote_f ();
440 Real staffline_f = paper ()->rule_thickness ();
441 Real beam_f = paper ()->beam_thickness_f ();
445 it would be nice to have all allowed positions in a runtime matrix:
446 (multiplicity, minimum_beam_dy, maximum_beam_dy)
450 Real sit = beam_f / 2 - staffline_f / 2;
451 Real inter = interline_f / 2;
452 Real hang = interline_f - beam_f / 2 + staffline_f / 2;
455 Put all allowed positions into an array.
456 Whether a position is allowed or not depends on
457 strictness of quantisation, multiplicity and direction.
459 For simplicity, we'll assume dir = UP and correct if
460 dir = DOWN afterwards.
463 // dim(left_y_) = internote
464 Real dy_f = dir_ * left_y_ * internote_f;
466 Real beamdx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
467 Real beamdy_f = beamdx_f * slope_f_ * internote_f;
469 Array<Real> allowed_position;
470 if (quantisation_ != TEST)
472 if (quantisation_ <= NORMAL)
474 if ((multiple_i_ <= 2) || (abs (beamdy_f) >= staffline_f / 2))
475 allowed_position.push (straddle);
476 if ((multiple_i_ <= 1) || (abs (beamdy_f) >= staffline_f / 2))
477 allowed_position.push (sit);
478 allowed_position.push (hang);
481 // TODO: check and fix TRADITIONAL
483 if ((multiple_i_ <= 2) || (abs (beamdy_f) >= staffline_f / 2))
484 allowed_position.push (straddle);
485 if ((multiple_i_ <= 1) && (beamdy_f <= staffline_f / 2))
486 allowed_position.push (sit);
487 if (beamdy_f >= -staffline_f / 2)
488 allowed_position.push (hang);
495 allowed_position.push (hang);
496 cout << "hang" << hang << "\n";
498 else if (test_pos==1)
500 allowed_position.push (straddle);
501 cout << "straddle" << straddle << endl;
503 else if (test_pos==2)
505 allowed_position.push (sit);
506 cout << "sit" << sit << endl;
508 else if (test_pos==3)
510 allowed_position.push (inter);
511 cout << "inter" << inter << endl;
515 Interval iv = quantise_iv (allowed_position, interline_f, dy_f);
517 Real quanty_f = dy_f - iv.min () <= iv.max () - dy_f ? iv.min () : iv.max ();
519 quanty_f = iv.max ();
521 // dim(left_y_) = internote
522 left_y_ = dir_ * quanty_f / internote_f;
526 Beam::set_stemlens ()
528 Real staffline_f = paper ()->rule_thickness ();
530 Real epsilon_f = staffline_f / 8;
532 DOUT << "Beam::set_stemlens: \n";
533 Real dy_f = check_stemlengths_f (false);
534 for (int i = 0; i < 2; i++)
536 left_y_ += dy_f * dir_;
537 quantise_left_y (dy_f);
538 dy_f = check_stemlengths_f (true);
539 if (abs (dy_f) <= epsilon_f)
541 DOUT << "Beam::set_stemlens: " << i << " iterations\n";
552 ugh. this is broken and should be rewritten.
556 Beam::set_grouping (Rhythmic_grouping def, Rhythmic_grouping cur)
560 assert (cur.children.size () == stems_.size ());
567 for (int j=0; j <stems_.size (); j++)
571 int f = s->flag_i_ - 2;
576 b= cur.generate_beams (flags, fi);
579 assert (stems_.size () == b.size ()/2);
582 for (int j=0, i=0; i < b.size () && j <stems_.size (); j++)
587 if (s->beams_i_drul_[d] < 0)
588 s->beams_i_drul_[d] = b[i];
590 multiple_i_ = multiple_i_ >? s->beams_i_drul_[d];
592 } while ((flip (&d)) != LEFT);
597 beams to go with one stem.
600 Beam::stem_beams (Stem *here, Stem *next, Stem *prev) const
602 assert (!next || next->hpos_f () > here->hpos_f ());
603 assert (!prev || prev->hpos_f () < here->hpos_f ());
605 Real staffline_f = paper ()->rule_thickness ();
606 Real interbeam_f = paper ()->interbeam_f (multiple_i_);
607 Real internote_f = paper ()->internote_f ();
608 Real beam_f = paper ()->beam_thickness_f ();
610 Real dy = interbeam_f;
611 Real stemdx = staffline_f;
612 Real sl = slope_f_* internote_f;
613 lookup_l ()->beam (sl, 20 PT, 1 PT);
619 Real nw_f = paper ()->note_width () * 0.8;
621 /* half beams extending to the left. */
624 int lhalfs= lhalfs = here->beams_i_drul_[LEFT] - prev->beams_i_drul_[RIGHT] ;
625 int lwholebeams= here->beams_i_drul_[LEFT] <? prev->beams_i_drul_[RIGHT] ;
627 Half beam should be one note-width,
628 but let's make sure two half-beams never touch
630 Real w = here->hpos_f () - prev->hpos_f ();
633 if (lhalfs) // generates warnings if not
634 a = lookup_l ()->beam (sl, w, beam_f);
635 a.translate (Offset (-w, -w * sl));
636 for (int j = 0; j < lhalfs; j++)
639 b.translate_axis (-dir_ * dy * (lwholebeams+j), Y_AXIS);
640 leftbeams.add_molecule (b);
646 int rhalfs = here->beams_i_drul_[RIGHT] - next->beams_i_drul_[LEFT];
647 int rwholebeams = here->beams_i_drul_[RIGHT] <? next->beams_i_drul_[LEFT];
649 Real w = next->hpos_f () - here->hpos_f ();
650 Molecule a = lookup_l ()->beam (sl, w + stemdx, beam_f);
651 a.translate_axis( - stemdx/2, X_AXIS);
654 if (here->beam_gap_i_)
656 int nogap = rwholebeams - here->beam_gap_i_;
657 for (; j < nogap; j++)
660 b.translate_axis (-dir_ * dy * j, Y_AXIS);
661 rightbeams.add_molecule (b);
663 // TODO: notehead widths differ for different types
666 a = lookup_l ()->beam (sl, w + stemdx, beam_f);
669 for (; j < rwholebeams; j++)
672 b.translate (Offset (gap_f, -dir_ * dy * j));
673 rightbeams.add_molecule (b);
678 a = lookup_l ()->beam (sl, w, beam_f);
680 for (; j < rwholebeams + rhalfs; j++)
683 b.translate_axis (-dir_ * dy * j, Y_AXIS);
684 rightbeams.add_molecule (b);
688 leftbeams.add_molecule (rightbeams);
691 Does beam quanting think of the asymetry of beams?
692 Refpoint is on bottom of symbol. (FIXTHAT) --hwn.