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 internote_f = paper ()->internote_f ();
273 int stem_max = (int)rint(paper ()->get_var ("stem_max"));
274 Real shorten_f = paper ()->get_var (String ("forced_stem_shorten"
275 + to_str (multiple_i_ <? stem_max)))
279 for (int i=0; i < stems_.size (); i++)
282 if (s->invisible_b ())
289 if (info.dir_ == dir_)
291 if (forced_count_i == total_count_i)
292 info.idealy_f_ -= shorten_f;
293 else if (forced_count_i > total_count_i / 2)
294 info.idealy_f_ -= shorten_f / 2;
301 Beam::calculate_slope ()
305 slope_f_ = left_y_ = 0;
306 else if (sinfo_[0].idealy_f_ == sinfo_.top ().idealy_f_)
309 left_y_ = sinfo_[0].idealy_f_;
315 Real solved_slope_f = slope_f_;
318 steep slope running against lengthened stem is suspect
320 Real dx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
322 // urg, these y internote-y-dimensions
323 Real internote_f = paper ()->internote_f ();
324 Real lengthened = paper ()->get_var ("beam_lengthened") / internote_f;
325 Real steep = paper ()->get_var ("beam_steep_slope") / internote_f;
326 if (((left_y_ - sinfo_[0].idealy_f_ > lengthened)
327 && (slope_f_ > steep))
328 || ((left_y_ + slope_f_ * dx_f - sinfo_.top ().idealy_f_ > lengthened)
329 && (slope_f_ < -steep)))
335 This neat trick is by Werner Lemberg,
336 damped = tanh (slope_f_)
337 corresponds with some tables in [Wanske]
340 slope_f_ = 0.6 * tanh (slope_f_) / damping_i_;
344 Real damped_slope_dy_f = (solved_slope_f - slope_f_) * dx_f / 2;
345 left_y_ += damped_slope_dy_f;
356 [Ross] (simplification of)
357 Try to set slope_f_ complying with y-span of:
359 - beam_f / 2 + staffline_f / 2
360 - beam_f + staffline_f
364 if (quantisation_ <= NONE)
367 Real interline_f = paper ()->interline_f ();
368 Real internote_f = interline_f / 2;
369 Real staffline_f = paper ()->rule_thickness ();
370 Real beam_f = paper ()->beam_thickness_f ();
372 Real dx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
374 // dim(y) = internote; so slope = (y/internote)/x
375 Real dy_f = dx_f * abs (slope_f_ * internote_f);
379 /* UGR. ICE in 2.8.1; bugreport filed. */
380 Array<Real> allowed_fraction (3);
381 allowed_fraction[0] = 0;
382 allowed_fraction[1] = (beam_f / 2 + staffline_f / 2);
383 allowed_fraction[2] = (beam_f + staffline_f);
386 Interval iv = quantise_iv (allowed_fraction, interline_f, dy_f);
387 quanty_f = (dy_f - iv.min () <= iv.max () - dy_f)
392 slope_f_ = (quanty_f / dx_f) / internote_f * sign (slope_f_);
395 static int test_pos = 0;
400 Prevent interference from stafflines and beams. See Documentation/tex/fonts.doc
404 Beam::quantise_left_y (bool extend_b)
407 we only need to quantise the start of the beam as dy is quantised too
408 if extend_b then stems must *not* get shorter
411 if (quantisation_ <= NONE)
415 ----------------------------------------------------------
419 --------------########------------------------------------
422 hang straddle sit inter hang
425 Real interline_f = paper ()->interline_f ();
426 Real internote_f = paper ()->internote_f ();
427 Real staffline_f = paper ()->rule_thickness ();
428 Real beam_f = paper ()->beam_thickness_f ();
432 it would be nice to have all allowed positions in a runtime matrix:
433 (multiplicity, minimum_beam_dy, maximum_beam_dy)
437 Real sit = beam_f / 2 - staffline_f / 2;
438 Real inter = interline_f / 2;
439 Real hang = interline_f - beam_f / 2 + staffline_f / 2;
442 Put all allowed positions into an array.
443 Whether a position is allowed or not depends on
444 strictness of quantisation, multiplicity and direction.
446 For simplicity, we'll assume dir = UP and correct if
447 dir = DOWN afterwards.
450 // dim(left_y_) = internote
451 Real dy_f = dir_ * left_y_ * internote_f;
453 Real beamdx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
454 Real beamdy_f = beamdx_f * slope_f_ * internote_f;
456 Array<Real> allowed_position;
457 if (quantisation_ != TEST)
459 if (quantisation_ <= NORMAL)
461 if ((multiple_i_ <= 2) || (abs (beamdy_f) >= staffline_f / 2))
462 allowed_position.push (straddle);
463 if ((multiple_i_ <= 1) || (abs (beamdy_f) >= staffline_f / 2))
464 allowed_position.push (sit);
465 allowed_position.push (hang);
468 // TODO: check and fix TRADITIONAL
470 if ((multiple_i_ <= 2) || (abs (beamdy_f) >= staffline_f / 2))
471 allowed_position.push (straddle);
472 if ((multiple_i_ <= 1) && (beamdy_f <= staffline_f / 2))
473 allowed_position.push (sit);
474 if (beamdy_f >= -staffline_f / 2)
475 allowed_position.push (hang);
482 allowed_position.push (hang);
483 cout << "hang" << hang << "\n";
485 else if (test_pos==1)
487 allowed_position.push (straddle);
488 cout << "straddle" << straddle << endl;
490 else if (test_pos==2)
492 allowed_position.push (sit);
493 cout << "sit" << sit << endl;
495 else if (test_pos==3)
497 allowed_position.push (inter);
498 cout << "inter" << inter << endl;
502 Interval iv = quantise_iv (allowed_position, interline_f, dy_f);
504 Real quanty_f = dy_f - iv.min () <= iv.max () - dy_f ? iv.min () : iv.max ();
506 quanty_f = iv.max ();
508 // dim(left_y_) = internote
509 left_y_ = dir_ * quanty_f / internote_f;
513 Beam::set_stemlens ()
515 Real staffline_f = paper ()->rule_thickness ();
517 Real epsilon_f = staffline_f / 8;
519 DOUT << "Beam::set_stemlens: \n";
520 Real dy_f = check_stemlengths_f (false);
521 for (int i = 0; i < 2; i++)
523 left_y_ += dy_f * dir_;
524 quantise_left_y (dy_f);
525 dy_f = check_stemlengths_f (true);
526 if (abs (dy_f) <= epsilon_f)
528 DOUT << "Beam::set_stemlens: " << i << " iterations\n";
539 ugh. this is broken and should be rewritten.
543 Beam::set_grouping (Rhythmic_grouping def, Rhythmic_grouping cur)
547 assert (cur.children.size () == stems_.size ());
554 for (int j=0; j <stems_.size (); j++)
558 int f = s->flag_i_ - 2;
563 b= cur.generate_beams (flags, fi);
566 assert (stems_.size () == b.size ()/2);
569 for (int j=0, i=0; i < b.size () && j <stems_.size (); j++)
574 if (s->beams_i_drul_[d] < 0)
575 s->beams_i_drul_[d] = b[i];
577 multiple_i_ = multiple_i_ >? s->beams_i_drul_[d];
579 } while ((flip (&d)) != LEFT);
584 beams to go with one stem.
587 Beam::stem_beams (Stem *here, Stem *next, Stem *prev) const
589 assert (!next || next->hpos_f () > here->hpos_f ());
590 assert (!prev || prev->hpos_f () < here->hpos_f ());
592 Real staffline_f = paper ()->rule_thickness ();
593 Real interbeam_f = paper ()->interbeam_f (multiple_i_);
594 Real internote_f = paper ()->internote_f ();
595 Real beam_f = paper ()->beam_thickness_f ();
597 Real dy = interbeam_f;
598 Real stemdx = staffline_f;
599 Real sl = slope_f_* internote_f;
600 lookup_l ()->beam (sl, 20 PT, 1 PT);
606 Real nw_f = paper ()->note_width () * 0.8;
608 /* half beams extending to the left. */
611 int lhalfs= lhalfs = here->beams_i_drul_[LEFT] - prev->beams_i_drul_[RIGHT] ;
612 int lwholebeams= here->beams_i_drul_[LEFT] <? prev->beams_i_drul_[RIGHT] ;
614 Half beam should be one note-width,
615 but let's make sure two half-beams never touch
617 Real w = here->hpos_f () - prev->hpos_f ();
620 if (lhalfs) // generates warnings if not
621 a = lookup_l ()->beam (sl, w, beam_f);
622 a.translate (Offset (-w, -w * sl));
623 for (int j = 0; j < lhalfs; j++)
626 b.translate_axis (-dir_ * dy * (lwholebeams+j), Y_AXIS);
627 leftbeams.add_atom (b);
633 int rhalfs = here->beams_i_drul_[RIGHT] - next->beams_i_drul_[LEFT];
634 int rwholebeams = here->beams_i_drul_[RIGHT] <? next->beams_i_drul_[LEFT];
636 Real w = next->hpos_f () - here->hpos_f ();
637 Atom a = lookup_l ()->beam (sl, w + stemdx, beam_f);
638 a.translate_axis( - stemdx/2, X_AXIS);
641 if (here->beam_gap_i_)
643 int nogap = rwholebeams - here->beam_gap_i_;
644 for (; j < nogap; j++)
647 b.translate_axis (-dir_ * dy * j, Y_AXIS);
648 rightbeams.add_atom (b);
650 // TODO: notehead widths differ for different types
653 a = lookup_l ()->beam (sl, w + stemdx, beam_f);
656 for (; j < rwholebeams; j++)
659 b.translate (Offset (gap_f, -dir_ * dy * j));
660 rightbeams.add_atom (b);
665 a = lookup_l ()->beam (sl, w, beam_f);
667 for (; j < rwholebeams + rhalfs; j++)
670 b.translate_axis (-dir_ * dy * j, Y_AXIS);
671 rightbeams.add_atom (b);
675 leftbeams.add_molecule (rightbeams);
678 Does beam quanting think of the asymetry of beams?
679 Refpoint is on bottom of symbol. (FIXTHAT) --hwn.