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 "grouping.hh"
42 quantisation_ = NORMAL;
47 Beam::add_stem (Stem*s)
50 s->add_dependency (this);
53 if (!spanned_drul_[LEFT])
60 Beam::do_brew_molecule_p () const
62 Molecule *mol_p = new Molecule;
63 Real internote_f = paper ()->internote_f ();
65 Real x0 = stems_[0]->hpos_f ();
66 for (int j=0; j <stems_.size (); j++)
69 Stem * prev = (j > 0)? stems_[j-1] : 0;
70 Stem * next = (j < stems_.size ()-1) ? stems_[j+1] :0;
72 Molecule sb = stem_beams (i, next, prev);
73 Real x = i->hpos_f ()-x0;
74 sb.translate (Offset (x, (x * slope_f_ + left_y_) * internote_f));
75 mol_p->add_molecule (sb);
77 mol_p->translate_axis (x0
78 - spanned_drul_[LEFT]->absolute_coordinate (X_AXIS), X_AXIS);
86 Real w= (paper ()->note_width () + extent (X_AXIS).length ())/2.0;
87 return Offset (w, (left_y_ + w* slope_f_)*paper ()->internote_f ());
91 Beam::do_pre_processing ()
98 Beam::do_print () const
101 DOUT << "slope_f_ " << slope_f_ << "left ypos " << left_y_;
102 Spanner::do_print ();
107 Beam::do_post_processing ()
109 if (stems_.size () < 2)
111 warning (_ ("beam with less than two stems"));
112 transparent_b_ = true;
120 Beam::do_substitute_dependent (Score_element*o,Score_element*n)
122 if (Stem * os = dynamic_cast<Stem*> (o))
123 stems_.substitute (os,
124 dynamic_cast<Stem *> (n));
128 Beam::do_width () const
130 return Interval (stems_[0]->hpos_f (),
131 stems_.top ()->hpos_f ());
135 Beam::set_default_dir ()
137 Drul_array<int> total;
138 total[UP] = total[DOWN] = 0;
139 Drul_array<int> count;
140 count[UP] = count[DOWN] = 0;
143 for (int i=0; i <stems_.size (); i++)
146 int current = s->dir_
147 ? (1 + d * s->dir_)/2
148 : s->get_center_distance ((Direction)-d);
156 } while (flip(&d) != DOWN);
159 [Ross] states that the majority of the notes dictates the
160 direction (and not the mean of "center distance")
162 But is that because it really looks better, or because he
163 wants to provide some real simple hands-on rules.
165 We have our doubts, so we simply provide all sensible alternatives.
168 Dir_algorithm a = (Dir_algorithm)rint(paper ()->get_var ("beam_dir_algorithm"));
172 dir_ = (count[UP] > count[DOWN]) ? UP : DOWN;
175 // mean centre distance
176 dir_ = (total[UP] > total[DOWN]) ? UP : DOWN;
180 // median centre distance
183 else if (!count[DOWN])
186 dir_ = (total[UP] / count[UP] > total[DOWN] / count[DOWN]) ? UP : DOWN;
190 for (int i=0; i <stems_.size (); i++)
194 if (!s->dir_forced_b_)
200 See Documentation/tex/fonts.doc
207 should use minimum energy formulation (cf linespacing)
209 assert (sinfo_.size () > 1);
210 DOUT << "Beam::solve_slope: \n";
213 for (int i=0; i < sinfo_.size (); i++)
215 l.input.push (Offset (sinfo_[i].x_, sinfo_[i].idealy_f_));
217 l.minimise (slope_f_, left_y_);
221 Beam::check_stemlengths_f (bool set_b)
223 Real interbeam_f = paper ()->interbeam_f (multiple_i_);
224 Real internote_f = paper ()->internote_f ();
225 Real beam_f = paper ()->beam_thickness_f ();
226 Real staffline_f = paper ()->rule_thickness ();
227 Real epsilon_f = staffline_f / 8;
229 for (int i=0; i < sinfo_.size (); i++)
231 Real y = sinfo_[i].x_ * slope_f_ + left_y_;
233 if (dir_ != sinfo_[i].dir_)
234 y -= dir_ * (beam_f / 2
235 + (sinfo_[i].mult_i_ - 1) * interbeam_f) / internote_f;
238 sinfo_[i].stem_l_->set_stemend (y);
241 if (y > sinfo_[i].maxy_f_)
242 dy_f = dy_f <? sinfo_[i].maxy_f_ - y;
243 if (y < sinfo_[i].miny_f_)
245 // when all too short, normal stems win..
246 if (dy_f < -epsilon_f)
247 warning (_ ("weird beam shift, check your knees"));
248 dy_f = dy_f >? sinfo_[i].miny_f_ - y;
255 Beam::set_steminfo ()
257 assert (multiple_i_);
258 int total_count_i = 0;
259 int forced_count_i = 0;
260 for (int i=0; i < stems_.size (); i++)
263 s->mult_i_ = multiple_i_;
264 s->set_default_extents ();
265 if (s->invisible_b ())
267 if (((int)s->chord_start_f ()) && (s->dir_ != s->get_default_dir ()))
272 Real break_i = (int)rint (paper ()->get_var ("beam_forced_multiple_break"));
274 if (multiple_i_ < break_i)
275 shorten_f = paper ()->get_var ("beam_forced_stem_shorten1");
277 shorten_f = paper ()->get_var ("beam_forced_stem_shorten2");
279 Real internote_f = paper ()->internote_f ();
281 for (int i=0; i < stems_.size (); i++)
284 if (s->invisible_b ())
291 if (info.dir_ == dir_)
293 if (forced_count_i == total_count_i)
294 info.idealy_f_ -= shorten_f / internote_f;
295 else if (forced_count_i > total_count_i / 2)
296 info.idealy_f_ -= shorten_f / 2 / internote_f;
303 Beam::calculate_slope ()
307 slope_f_ = left_y_ = 0;
308 else if (sinfo_[0].idealy_f_ == sinfo_.top ().idealy_f_)
311 left_y_ = sinfo_[0].idealy_f_;
317 Real solved_slope_f = slope_f_;
320 steep slope running against lengthened stem is suspect
322 Real dx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
324 // urg, these y internote-y-dimensions
325 Real internote_f = paper ()->internote_f ();
326 Real lengthened = paper ()->get_var ("beam_lengthened") / internote_f;
327 Real steep = paper ()->get_var ("beam_steep_slope") / internote_f;
328 if (((left_y_ - sinfo_[0].idealy_f_ > lengthened)
329 && (slope_f_ > steep))
330 || ((left_y_ + slope_f_ * dx_f - sinfo_.top ().idealy_f_ > lengthened)
331 && (slope_f_ < -steep)))
337 This neat trick is by Werner Lemberg,
338 damped = tanh (slope_f_)
339 corresponds with some tables in [Wanske]
342 slope_f_ = 0.6 * tanh (slope_f_) / damping_i_;
346 Real damped_slope_dy_f = (solved_slope_f - slope_f_) * dx_f / 2;
347 left_y_ += damped_slope_dy_f;
358 [Ross] (simplification of)
359 Try to set slope_f_ complying with y-span of:
361 - beam_f / 2 + staffline_f / 2
362 - beam_f + staffline_f
366 if (quantisation_ <= NONE)
369 Real interline_f = paper ()->interline_f ();
370 Real internote_f = interline_f / 2;
371 Real staffline_f = paper ()->rule_thickness ();
372 Real beam_f = paper ()->beam_thickness_f ();
374 Real dx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
376 // dim(y) = internote; so slope = (y/internote)/x
377 Real dy_f = dx_f * abs (slope_f_ * internote_f);
381 /* UGR. ICE in 2.8.1; bugreport filed. */
382 Array<Real> allowed_fraction (3);
383 allowed_fraction[0] = 0;
384 allowed_fraction[1] = (beam_f / 2 + staffline_f / 2);
385 allowed_fraction[2] = (beam_f + staffline_f);
388 Interval iv = quantise_iv (allowed_fraction, interline_f, dy_f);
389 quanty_f = (dy_f - iv.min () <= iv.max () - dy_f)
394 slope_f_ = (quanty_f / dx_f) / internote_f * sign (slope_f_);
397 static int test_pos = 0;
402 Prevent interference from stafflines and beams. See Documentation/tex/fonts.doc
406 Beam::quantise_left_y (bool extend_b)
409 we only need to quantise the start of the beam as dy is quantised too
410 if extend_b then stems must *not* get shorter
413 if (quantisation_ <= NONE)
417 ----------------------------------------------------------
421 --------------########------------------------------------
424 hang straddle sit inter hang
427 Real interline_f = paper ()->interline_f ();
428 Real internote_f = paper ()->internote_f ();
429 Real staffline_f = paper ()->rule_thickness ();
430 Real beam_f = paper ()->beam_thickness_f ();
434 it would be nice to have all allowed positions in a runtime matrix:
435 (multiplicity, minimum_beam_dy, maximum_beam_dy)
439 Real sit = beam_f / 2 - staffline_f / 2;
440 Real inter = interline_f / 2;
441 Real hang = interline_f - beam_f / 2 + staffline_f / 2;
444 Put all allowed positions into an array.
445 Whether a position is allowed or not depends on
446 strictness of quantisation, multiplicity and direction.
448 For simplicity, we'll assume dir = UP and correct if
449 dir = DOWN afterwards.
452 // dim(left_y_) = internote
453 Real dy_f = dir_ * left_y_ * internote_f;
455 Real beamdx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
456 Real beamdy_f = beamdx_f * slope_f_ * internote_f;
458 Array<Real> allowed_position;
459 if (quantisation_ != TEST)
461 if (quantisation_ <= NORMAL)
463 if ((multiple_i_ <= 2) || (abs (beamdy_f) >= staffline_f / 2))
464 allowed_position.push (straddle);
465 if ((multiple_i_ <= 1) || (abs (beamdy_f) >= staffline_f / 2))
466 allowed_position.push (sit);
467 allowed_position.push (hang);
470 // TODO: check and fix TRADITIONAL
472 if ((multiple_i_ <= 2) || (abs (beamdy_f) >= staffline_f / 2))
473 allowed_position.push (straddle);
474 if ((multiple_i_ <= 1) && (beamdy_f <= staffline_f / 2))
475 allowed_position.push (sit);
476 if (beamdy_f >= -staffline_f / 2)
477 allowed_position.push (hang);
484 allowed_position.push (hang);
485 cout << "hang" << hang << "\n";
487 else if (test_pos==1)
489 allowed_position.push (straddle);
490 cout << "straddle" << straddle << endl;
492 else if (test_pos==2)
494 allowed_position.push (sit);
495 cout << "sit" << sit << endl;
497 else if (test_pos==3)
499 allowed_position.push (inter);
500 cout << "inter" << inter << endl;
504 Interval iv = quantise_iv (allowed_position, interline_f, dy_f);
506 Real quanty_f = dy_f - iv.min () <= iv.max () - dy_f ? iv.min () : iv.max ();
508 quanty_f = iv.max ();
510 // dim(left_y_) = internote
511 left_y_ = dir_ * quanty_f / internote_f;
515 Beam::set_stemlens ()
517 Real staffline_f = paper ()->rule_thickness ();
519 Real epsilon_f = staffline_f / 8;
521 DOUT << "Beam::set_stemlens: \n";
522 Real dy_f = check_stemlengths_f (false);
523 for (int i = 0; i < 2; i++)
525 left_y_ += dy_f * dir_;
526 quantise_left_y (dy_f);
527 dy_f = check_stemlengths_f (true);
528 if (abs (dy_f) <= epsilon_f)
530 DOUT << "Beam::set_stemlens: " << i << " iterations\n";
541 ugh. this is broken and should be rewritten.
545 Beam::set_grouping (Rhythmic_grouping def, Rhythmic_grouping cur)
549 assert (cur.children.size () == stems_.size ());
556 for (int j=0; j <stems_.size (); j++)
560 int f = s->flag_i_ - 2;
565 b= cur.generate_beams (flags, fi);
568 assert (stems_.size () == b.size ()/2);
571 for (int j=0, i=0; i < b.size () && j <stems_.size (); j++)
576 if (s->beams_i_drul_[d] < 0)
577 s->beams_i_drul_[d] = b[i];
579 multiple_i_ = multiple_i_ >? s->beams_i_drul_[d];
581 } while ((flip (&d)) != LEFT);
586 beams to go with one stem.
589 Beam::stem_beams (Stem *here, Stem *next, Stem *prev) const
591 assert (!next || next->hpos_f () > here->hpos_f ());
592 assert (!prev || prev->hpos_f () < here->hpos_f ());
594 Real staffline_f = paper ()->rule_thickness ();
595 Real interbeam_f = paper ()->interbeam_f (multiple_i_);
596 Real internote_f = paper ()->internote_f ();
597 Real beam_f = paper ()->beam_thickness_f ();
599 Real dy = interbeam_f;
600 Real stemdx = staffline_f;
601 Real sl = slope_f_* internote_f;
602 lookup_l ()->beam (sl, 20 PT, 1 PT);
608 Real nw_f = paper ()->note_width () * 0.8;
610 /* half beams extending to the left. */
613 int lhalfs= lhalfs = here->beams_i_drul_[LEFT] - prev->beams_i_drul_[RIGHT] ;
614 int lwholebeams= here->beams_i_drul_[LEFT] <? prev->beams_i_drul_[RIGHT] ;
616 Half beam should be one note-width,
617 but let's make sure two half-beams never touch
619 Real w = here->hpos_f () - prev->hpos_f ();
622 if (lhalfs) // generates warnings if not
623 a = lookup_l ()->beam (sl, w, beam_f);
624 a.translate (Offset (-w, -w * sl));
625 for (int j = 0; j < lhalfs; j++)
628 b.translate_axis (-dir_ * dy * (lwholebeams+j), Y_AXIS);
629 leftbeams.add_atom (b);
635 int rhalfs = here->beams_i_drul_[RIGHT] - next->beams_i_drul_[LEFT];
636 int rwholebeams = here->beams_i_drul_[RIGHT] <? next->beams_i_drul_[LEFT];
638 Real w = next->hpos_f () - here->hpos_f ();
639 Atom a = lookup_l ()->beam (sl, w + stemdx, beam_f);
640 a.translate_axis( - stemdx/2, X_AXIS);
643 if (here->beam_gap_i_)
645 int nogap = rwholebeams - here->beam_gap_i_;
646 for (; j < nogap; j++)
649 b.translate_axis (-dir_ * dy * j, Y_AXIS);
650 rightbeams.add_atom (b);
652 // TODO: notehead widths differ for different types
655 a = lookup_l ()->beam (sl, w + stemdx, beam_f);
658 for (; j < rwholebeams; j++)
661 b.translate (Offset (gap_f, -dir_ * dy * j));
662 rightbeams.add_atom (b);
667 a = lookup_l ()->beam (sl, w, beam_f);
669 for (; j < rwholebeams + rhalfs; j++)
672 b.translate_axis (-dir_ * dy * j, Y_AXIS);
673 rightbeams.add_atom (b);
677 leftbeams.add_molecule (rightbeams);
680 Does beam quanting think of the asymetry of beams?
681 Refpoint is on bottom of symbol. (FIXTHAT) --hwn.