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);
80 // correct if last note (and therefore reference point of beam)
81 // is on different staff
82 mol_p->translate_axis (- sinfo_.top ().interstaff_f_ * internote_f, Y_AXIS);
90 Real w= (paper ()->note_width () + extent (X_AXIS).length ())/2.0;
91 return Offset (w, (left_y_ + w* slope_f_)*paper ()->internote_f ());
95 Beam::do_pre_processing ()
102 Beam::do_print () const
105 DOUT << "slope_f_ " << slope_f_ << "left ypos " << left_y_;
106 Spanner::do_print ();
111 Beam::do_post_processing ()
113 if (stems_.size () < 2)
115 warning (_ ("beam with less than two stems"));
116 transparent_b_ = true;
124 Beam::do_substitute_dependent (Score_element*o,Score_element*n)
126 if (Stem * os = dynamic_cast<Stem*> (o))
127 stems_.substitute (os,
128 dynamic_cast<Stem *> (n));
132 Beam::do_width () const
134 return Interval (stems_[0]->hpos_f (),
135 stems_.top ()->hpos_f ());
139 Beam::set_default_dir ()
141 Drul_array<int> total;
142 total[UP] = total[DOWN] = 0;
143 Drul_array<int> count;
144 count[UP] = count[DOWN] = 0;
147 for (int i=0; i <stems_.size (); i++)
150 int current = s->dir_
151 ? (1 + d * s->dir_)/2
152 : s->get_center_distance ((Direction)-d);
160 } while (flip(&d) != DOWN);
163 [Ross] states that the majority of the notes dictates the
164 direction (and not the mean of "center distance")
166 But is that because it really looks better, or because he
167 wants to provide some real simple hands-on rules.
169 We have our doubts, so we simply provide all sensible alternatives.
172 Dir_algorithm a = (Dir_algorithm)rint(paper ()->get_var ("beam_dir_algorithm"));
176 dir_ = (count[UP] > count[DOWN]) ? UP : DOWN;
179 // mean centre distance
180 dir_ = (total[UP] > total[DOWN]) ? UP : DOWN;
184 // median centre distance
187 else if (!count[DOWN])
190 dir_ = (total[UP] / count[UP] > total[DOWN] / count[DOWN]) ? UP : DOWN;
194 for (int i=0; i <stems_.size (); i++)
198 if (!s->dir_forced_b_)
204 See Documentation/tex/fonts.doc
211 should use minimum energy formulation (cf linespacing)
213 assert (sinfo_.size () > 1);
214 DOUT << "Beam::solve_slope: \n";
217 for (int i=0; i < sinfo_.size (); i++)
219 l.input.push (Offset (sinfo_[i].x_, sinfo_[i].idealy_f_));
221 l.minimise (slope_f_, left_y_);
225 Beam::check_stemlengths_f (bool set_b)
227 Real interbeam_f = paper ()->interbeam_f (multiple_i_);
228 Real internote_f = paper ()->internote_f ();
229 Real beam_f = paper ()->beam_thickness_f ();
230 Real staffline_f = paper ()->rule_thickness ();
231 Real epsilon_f = staffline_f / 8;
233 for (int i=0; i < sinfo_.size (); i++)
235 Real y = sinfo_[i].x_ * slope_f_ + left_y_;
238 if (dir_ != sinfo_[i].dir_)
240 y -= dir_ * (beam_f / 2
241 + (sinfo_[i].mult_i_ - 1) * interbeam_f) / internote_f;
242 if (!i && sinfo_[i].stem_l_->staff_sym_l_ !=
243 sinfo_.top ().stem_l_->staff_sym_l_)
244 y += dir_ * (multiple_i_ - (sinfo_[i].stem_l_->flag_i_ - 2) >? 0)
245 * interbeam_f / internote_f;
249 sinfo_[i].stem_l_->set_stemend (y - sinfo_[i].interstaff_f_);
252 if (y > sinfo_[i].maxy_f_)
253 dy_f = dy_f <? sinfo_[i].maxy_f_ - y;
254 if (y < sinfo_[i].miny_f_)
256 // when all too short, normal stems win..
257 if (dy_f < -epsilon_f)
258 warning (_ ("weird beam shift, check your knees"));
259 dy_f = dy_f >? sinfo_[i].miny_f_ - y;
266 Beam::set_steminfo ()
268 assert (multiple_i_);
269 int total_count_i = 0;
270 int forced_count_i = 0;
271 for (int i=0; i < stems_.size (); i++)
274 s->mult_i_ = multiple_i_;
275 s->set_default_extents ();
276 if (s->invisible_b ())
278 if (((int)s->chord_start_f ()) && (s->dir_ != s->get_default_dir ()))
283 Real internote_f = paper ()->internote_f ();
284 int stem_max = (int)rint(paper ()->get_var ("stem_max"));
285 Real shorten_f = paper ()->get_var (String ("forced_stem_shorten"
286 + to_str (multiple_i_ <? stem_max)))
290 for (int i=0; i < stems_.size (); i++)
293 if (s->invisible_b ())
300 if (info.dir_ == dir_)
302 if (forced_count_i == total_count_i)
303 info.idealy_f_ -= shorten_f;
304 else if (forced_count_i > total_count_i / 2)
305 info.idealy_f_ -= shorten_f / 2;
312 Beam::calculate_slope ()
316 slope_f_ = left_y_ = 0;
317 else if (sinfo_[0].idealy_f_ == sinfo_.top ().idealy_f_)
320 left_y_ = sinfo_[0].idealy_f_;
326 Real solved_slope_f = slope_f_;
329 steep slope running against lengthened stem is suspect
331 Real dx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
333 // urg, these y internote-y-dimensions
334 Real internote_f = paper ()->internote_f ();
335 Real lengthened = paper ()->get_var ("beam_lengthened") / internote_f;
336 Real steep = paper ()->get_var ("beam_steep_slope") / internote_f;
337 if (((left_y_ - sinfo_[0].idealy_f_ > lengthened)
338 && (slope_f_ > steep))
339 || ((left_y_ + slope_f_ * dx_f - sinfo_.top ().idealy_f_ > lengthened)
340 && (slope_f_ < -steep)))
346 This neat trick is by Werner Lemberg,
347 damped = tanh (slope_f_)
348 corresponds with some tables in [Wanske]
351 slope_f_ = 0.6 * tanh (slope_f_) / damping_i_;
355 Real damped_slope_dy_f = (solved_slope_f - slope_f_) * dx_f / 2;
356 left_y_ += damped_slope_dy_f;
367 [Ross] (simplification of)
368 Try to set slope_f_ complying with y-span of:
370 - beam_f / 2 + staffline_f / 2
371 - beam_f + staffline_f
375 if (quantisation_ <= NONE)
378 Real interline_f = paper ()->interline_f ();
379 Real internote_f = interline_f / 2;
380 Real staffline_f = paper ()->rule_thickness ();
381 Real beam_f = paper ()->beam_thickness_f ();
383 Real dx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
385 // dim(y) = internote; so slope = (y/internote)/x
386 Real dy_f = dx_f * abs (slope_f_ * internote_f);
390 /* UGR. ICE in 2.8.1; bugreport filed. */
391 Array<Real> allowed_fraction (3);
392 allowed_fraction[0] = 0;
393 allowed_fraction[1] = (beam_f / 2 + staffline_f / 2);
394 allowed_fraction[2] = (beam_f + staffline_f);
397 Interval iv = quantise_iv (allowed_fraction, interline_f, dy_f);
398 quanty_f = (dy_f - iv.min () <= iv.max () - dy_f)
403 slope_f_ = (quanty_f / dx_f) / internote_f * sign (slope_f_);
406 static int test_pos = 0;
411 Prevent interference from stafflines and beams. See Documentation/tex/fonts.doc
415 Beam::quantise_left_y (bool extend_b)
418 we only need to quantise the start of the beam as dy is quantised too
419 if extend_b then stems must *not* get shorter
422 if (quantisation_ <= NONE)
426 ----------------------------------------------------------
430 --------------########------------------------------------
433 hang straddle sit inter hang
436 Real interline_f = paper ()->interline_f ();
437 Real internote_f = paper ()->internote_f ();
438 Real staffline_f = paper ()->rule_thickness ();
439 Real beam_f = paper ()->beam_thickness_f ();
443 it would be nice to have all allowed positions in a runtime matrix:
444 (multiplicity, minimum_beam_dy, maximum_beam_dy)
448 Real sit = beam_f / 2 - staffline_f / 2;
449 Real inter = interline_f / 2;
450 Real hang = interline_f - beam_f / 2 + staffline_f / 2;
453 Put all allowed positions into an array.
454 Whether a position is allowed or not depends on
455 strictness of quantisation, multiplicity and direction.
457 For simplicity, we'll assume dir = UP and correct if
458 dir = DOWN afterwards.
461 // dim(left_y_) = internote
462 Real dy_f = dir_ * left_y_ * internote_f;
464 Real beamdx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
465 Real beamdy_f = beamdx_f * slope_f_ * internote_f;
467 Array<Real> allowed_position;
468 if (quantisation_ != TEST)
470 if (quantisation_ <= NORMAL)
472 if ((multiple_i_ <= 2) || (abs (beamdy_f) >= staffline_f / 2))
473 allowed_position.push (straddle);
474 if ((multiple_i_ <= 1) || (abs (beamdy_f) >= staffline_f / 2))
475 allowed_position.push (sit);
476 allowed_position.push (hang);
479 // TODO: check and fix TRADITIONAL
481 if ((multiple_i_ <= 2) || (abs (beamdy_f) >= staffline_f / 2))
482 allowed_position.push (straddle);
483 if ((multiple_i_ <= 1) && (beamdy_f <= staffline_f / 2))
484 allowed_position.push (sit);
485 if (beamdy_f >= -staffline_f / 2)
486 allowed_position.push (hang);
493 allowed_position.push (hang);
494 cout << "hang" << hang << "\n";
496 else if (test_pos==1)
498 allowed_position.push (straddle);
499 cout << "straddle" << straddle << endl;
501 else if (test_pos==2)
503 allowed_position.push (sit);
504 cout << "sit" << sit << endl;
506 else if (test_pos==3)
508 allowed_position.push (inter);
509 cout << "inter" << inter << endl;
513 Interval iv = quantise_iv (allowed_position, interline_f, dy_f);
515 Real quanty_f = dy_f - iv.min () <= iv.max () - dy_f ? iv.min () : iv.max ();
517 quanty_f = iv.max ();
519 // dim(left_y_) = internote
520 left_y_ = dir_ * quanty_f / internote_f;
524 Beam::set_stemlens ()
526 Real staffline_f = paper ()->rule_thickness ();
528 Real epsilon_f = staffline_f / 8;
530 DOUT << "Beam::set_stemlens: \n";
531 Real dy_f = check_stemlengths_f (false);
532 for (int i = 0; i < 2; i++)
534 left_y_ += dy_f * dir_;
535 quantise_left_y (dy_f);
536 dy_f = check_stemlengths_f (true);
537 if (abs (dy_f) <= epsilon_f)
539 DOUT << "Beam::set_stemlens: " << i << " iterations\n";
550 ugh. this is broken and should be rewritten.
554 Beam::set_grouping (Rhythmic_grouping def, Rhythmic_grouping cur)
558 assert (cur.children.size () == stems_.size ());
565 for (int j=0; j <stems_.size (); j++)
569 int f = s->flag_i_ - 2;
574 b= cur.generate_beams (flags, fi);
577 assert (stems_.size () == b.size ()/2);
580 for (int j=0, i=0; i < b.size () && j <stems_.size (); j++)
585 if (s->beams_i_drul_[d] < 0)
586 s->beams_i_drul_[d] = b[i];
588 multiple_i_ = multiple_i_ >? s->beams_i_drul_[d];
590 } while ((flip (&d)) != LEFT);
595 beams to go with one stem.
598 Beam::stem_beams (Stem *here, Stem *next, Stem *prev) const
600 assert (!next || next->hpos_f () > here->hpos_f ());
601 assert (!prev || prev->hpos_f () < here->hpos_f ());
603 Real staffline_f = paper ()->rule_thickness ();
604 Real interbeam_f = paper ()->interbeam_f (multiple_i_);
605 Real internote_f = paper ()->internote_f ();
606 Real beam_f = paper ()->beam_thickness_f ();
608 Real dy = interbeam_f;
609 Real stemdx = staffline_f;
610 Real sl = slope_f_* internote_f;
611 lookup_l ()->beam (sl, 20 PT, 1 PT);
617 Real nw_f = paper ()->note_width () * 0.8;
619 /* half beams extending to the left. */
622 int lhalfs= lhalfs = here->beams_i_drul_[LEFT] - prev->beams_i_drul_[RIGHT] ;
623 int lwholebeams= here->beams_i_drul_[LEFT] <? prev->beams_i_drul_[RIGHT] ;
625 Half beam should be one note-width,
626 but let's make sure two half-beams never touch
628 Real w = here->hpos_f () - prev->hpos_f ();
631 if (lhalfs) // generates warnings if not
632 a = lookup_l ()->beam (sl, w, beam_f);
633 a.translate (Offset (-w, -w * sl));
634 for (int j = 0; j < lhalfs; j++)
637 b.translate_axis (-dir_ * dy * (lwholebeams+j), Y_AXIS);
638 leftbeams.add_atom (b);
644 int rhalfs = here->beams_i_drul_[RIGHT] - next->beams_i_drul_[LEFT];
645 int rwholebeams = here->beams_i_drul_[RIGHT] <? next->beams_i_drul_[LEFT];
647 Real w = next->hpos_f () - here->hpos_f ();
648 Atom a = lookup_l ()->beam (sl, w + stemdx, beam_f);
649 a.translate_axis( - stemdx/2, X_AXIS);
652 if (here->beam_gap_i_)
654 int nogap = rwholebeams - here->beam_gap_i_;
655 for (; j < nogap; j++)
658 b.translate_axis (-dir_ * dy * j, Y_AXIS);
659 rightbeams.add_atom (b);
661 // TODO: notehead widths differ for different types
664 a = lookup_l ()->beam (sl, w + stemdx, beam_f);
667 for (; j < rwholebeams; j++)
670 b.translate (Offset (gap_f, -dir_ * dy * j));
671 rightbeams.add_atom (b);
676 a = lookup_l ()->beam (sl, w, beam_f);
678 for (; j < rwholebeams + rhalfs; j++)
681 b.translate_axis (-dir_ * dy * j, Y_AXIS);
682 rightbeams.add_atom (b);
686 leftbeams.add_molecule (rightbeams);
689 Does beam quanting think of the asymetry of beams?
690 Refpoint is on bottom of symbol. (FIXTHAT) --hwn.