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>
24 #include "dimensions.hh"
26 #include "abbreviation-beam.hh"
29 #include "molecule.hh"
30 #include "leastsquares.hh"
32 #include "paper-def.hh"
34 #include "rhythmic-grouping.hh"
40 quantisation_ = NORMAL;
42 vertical_align_drul_[MIN] = 0;
43 vertical_align_drul_[MAX] = -1;
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;
66 Real x0 = stems_[0]->hpos_f ();
67 for (int j=0; j <stems_.size (); j++)
70 Stem * prev = (j > 0)? stems_[j-1] : 0;
71 Stem * next = (j < stems_.size ()-1) ? stems_[j+1] :0;
73 Molecule sb = stem_beams (i, next, prev);
74 Real x = i->hpos_f ()-x0;
75 sb.translate (Offset (x, (x * slope_f_ + left_y_) *
76 i->staff_line_leading_f ()/2 ));
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 Stem_info si = sinfo_.top ();
85 mol_p->translate_axis (-si.interstaff_f_ * si.stem_l_->staff_line_leading_f ()/2,
94 Stem_info si = sinfo_[0];
96 Real w= (si.stem_l_->note_delta_f () + extent (X_AXIS).length ())/2.0;
97 return Offset (w, (left_y_ + w* slope_f_) *
98 si.stem_l_->staff_line_leading_f ()/2);
102 Beam::do_pre_processing ()
109 Beam::do_print () const
112 DOUT << "slope_f_ " << slope_f_ << "left ypos " << left_y_;
113 Spanner::do_print ();
118 Beam::do_post_processing ()
120 if (stems_.size () < 2)
122 warning (_ ("beam with less than two stems"));
123 set_elt_property (transparent_scm_sym, SCM_BOOL_T);
131 Beam::do_substitute_element_pointer (Score_element*o,Score_element*n)
133 if (Stem * os = dynamic_cast<Stem*> (o))
134 stems_.substitute (os,
135 dynamic_cast<Stem *> (n));
139 Beam::do_width () const
141 return Interval (stems_[0]->hpos_f (),
142 stems_.top ()->hpos_f ());
146 Beam::set_default_dir ()
148 Drul_array<int> total;
149 total[UP] = total[DOWN] = 0;
150 Drul_array<int> count;
151 count[UP] = count[DOWN] = 0;
154 for (int i=0; i <stems_.size (); i++)
157 int current = s->dir_
158 ? (1 + d * s->dir_)/2
159 : s->get_center_distance ((Direction)-d);
167 } while (flip(&d) != DOWN);
170 [Ross] states that the majority of the notes dictates the
171 direction (and not the mean of "center distance")
173 But is that because it really looks better, or because he
174 wants to provide some real simple hands-on rules.
176 We have our doubts, so we simply provide all sensible alternatives.
179 Dir_algorithm a = (Dir_algorithm)rint(paper_l ()->get_var ("beam_dir_algorithm"));
183 dir_ = (count[UP] > count[DOWN]) ? UP : DOWN;
186 // mean centre distance
187 dir_ = (total[UP] > total[DOWN]) ? UP : DOWN;
191 // median centre distance
194 else if (!count[DOWN])
197 dir_ = (total[UP] / count[UP] > total[DOWN] / count[DOWN]) ? UP : DOWN;
201 for (int i=0; i <stems_.size (); i++)
204 s->set_elt_property (beam_dir_scm_sym, gh_int2scm (dir_));
206 SCM force = s->remove_elt_property (dir_forced_scm_sym);
207 if (force == SCM_BOOL_F)
213 See Documentation/tex/fonts.doc
220 should use minimum energy formulation (cf linespacing)
222 assert (sinfo_.size () > 1);
223 DOUT << "Beam::solve_slope: \n";
226 for (int i=0; i < sinfo_.size (); i++)
228 l.input.push (Offset (sinfo_[i].x_, sinfo_[i].idealy_f_));
230 l.minimise (slope_f_, left_y_);
234 Beam::check_stemlengths_f (bool set_b)
236 Real interbeam_f = paper_l ()->interbeam_f (multiple_i_);
238 Real beam_f = paper_l ()->beam_thickness_f ();
239 Real staffline_f = paper_l ()->rule_thickness ();
240 Real epsilon_f = staffline_f / 8;
242 for (int i=0; i < sinfo_.size (); i++)
244 Real y = sinfo_[i].x_ * slope_f_ + left_y_;
247 if (dir_ != sinfo_[i].dir_)
249 Real internote_f = sinfo_[i].stem_l_->staff_line_leading_f ()/2;
250 y -= dir_ * (beam_f / 2
251 + (sinfo_[i].mult_i_ - 1) * interbeam_f) / internote_f;
252 if (!i && sinfo_[i].stem_l_->staff_symbol_l () !=
253 sinfo_.top ().stem_l_->staff_symbol_l ())
254 y += dir_ * (multiple_i_ - (sinfo_[i].stem_l_->flag_i_ - 2) >? 0)
255 * interbeam_f / internote_f;
259 sinfo_[i].stem_l_->set_stemend (y - sinfo_[i].interstaff_f_);
262 if (y > sinfo_[i].maxy_f_)
263 dy_f = dy_f <? sinfo_[i].maxy_f_ - y;
264 if (y < sinfo_[i].miny_f_)
266 // when all too short, normal stems win..
267 if (dy_f < -epsilon_f)
268 warning (_ ("weird beam shift, check your knees"));
269 dy_f = dy_f >? sinfo_[i].miny_f_ - y;
276 Beam::set_steminfo ()
281 assert (multiple_i_);
282 int total_count_i = 0;
283 int forced_count_i = 0;
284 for (int i=0; i < stems_.size (); i++)
287 s->mult_i_ = multiple_i_;
288 s->set_default_extents ();
289 if (s->invisible_b ())
291 if (((int)s->chord_start_f ()) && (s->dir_ != s->get_default_dir ()))
296 Real internote_f = stems_[0]->staff_line_leading_f ()/2;
297 int stem_max = (int)rint(paper_l ()->get_var ("stem_max"));
298 Real shorten_f = paper_l ()->get_var (String ("forced_stem_shorten"
299 + to_str (multiple_i_ <? stem_max)))
303 for (int i=0; i < stems_.size (); i++)
306 if (s->invisible_b ())
313 if (info.dir_ == dir_)
315 if (forced_count_i == total_count_i)
316 info.idealy_f_ -= shorten_f;
317 else if (forced_count_i > total_count_i / 2)
318 info.idealy_f_ -= shorten_f / 2;
325 Beam::calculate_slope ()
329 slope_f_ = left_y_ = 0;
330 else if (sinfo_[0].idealy_f_ == sinfo_.top ().idealy_f_)
333 left_y_ = sinfo_[0].idealy_f_;
339 Real solved_slope_f = slope_f_;
342 steep slope running against lengthened stem is suspect
344 Real dx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
346 // urg, these y internote-y-dimensions
347 Real internote_f = stems_[0]->staff_line_leading_f ()/2;
349 Real lengthened = paper_l ()->get_var ("beam_lengthened") / internote_f;
350 Real steep = paper_l ()->get_var ("beam_steep_slope") / internote_f;
351 if (((left_y_ - sinfo_[0].idealy_f_ > lengthened)
352 && (slope_f_ > steep))
353 || ((left_y_ + slope_f_ * dx_f - sinfo_.top ().idealy_f_ > lengthened)
354 && (slope_f_ < -steep)))
360 This neat trick is by Werner Lemberg,
361 damped = tanh (slope_f_)
362 corresponds with some tables in [Wanske]
364 SCM damp = remove_elt_property (damping_scm_sym);
365 int damping = 1; // ugh.
366 if (damp!= SCM_BOOL_F)
367 damping = gh_int2scm (SCM_CDR(damp));
370 slope_f_ = 0.6 * tanh (slope_f_) / damping;
374 Real damped_slope_dy_f = (solved_slope_f - slope_f_) * dx_f / 2;
375 left_y_ += damped_slope_dy_f;
386 [Ross] (simplification of)
387 Try to set slope_f_ complying with y-span of:
389 - beam_f / 2 + staffline_f / 2
390 - beam_f + staffline_f
394 if (quantisation_ <= NONE)
397 Real interline_f = stems_[0]->staff_line_leading_f ();
398 Real internote_f = interline_f / 2;
399 Real staffline_f = paper_l ()->rule_thickness ();
400 Real beam_f = paper_l ()->beam_thickness_f ();
402 Real dx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
404 // dim(y) = internote; so slope = (y/internote)/x
405 Real dy_f = dx_f * abs (slope_f_ * internote_f);
409 /* UGR. ICE in 2.8.1; bugreport filed. */
410 Array<Real> allowed_fraction (3);
411 allowed_fraction[0] = 0;
412 allowed_fraction[1] = (beam_f / 2 + staffline_f / 2);
413 allowed_fraction[2] = (beam_f + staffline_f);
416 Interval iv = quantise_iv (allowed_fraction, interline_f, dy_f);
417 quanty_f = (dy_f - iv.min () <= iv.max () - dy_f)
422 slope_f_ = (quanty_f / dx_f) / internote_f * sign (slope_f_);
425 static int test_pos = 0;
430 Prevent interference from stafflines and beams. See Documentation/tex/fonts.doc
434 Beam::quantise_left_y (bool extend_b)
437 we only need to quantise the start of the beam as dy is quantised too
438 if extend_b then stems must *not* get shorter
441 if (quantisation_ <= NONE)
445 ----------------------------------------------------------
449 --------------########------------------------------------
452 hang straddle sit inter hang
455 Real space = stems_[0]->staff_line_leading_f ();
456 Real internote_f = space /2;
457 Real staffline_f = paper_l ()->rule_thickness ();
458 Real beam_f = paper_l ()->beam_thickness_f ();
462 it would be nice to have all allowed positions in a runtime matrix:
463 (multiplicity, minimum_beam_dy, maximum_beam_dy)
467 Real sit = beam_f / 2 - staffline_f / 2;
468 Real inter = space / 2;
469 Real hang = space - beam_f / 2 + staffline_f / 2;
472 Put all allowed positions into an array.
473 Whether a position is allowed or not depends on
474 strictness of quantisation, multiplicity and direction.
476 For simplicity, we'll assume dir = UP and correct if
477 dir = DOWN afterwards.
480 // dim(left_y_) = internote
481 Real dy_f = dir_ * left_y_ * internote_f;
483 Real beamdx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
484 Real beamdy_f = beamdx_f * slope_f_ * internote_f;
486 Array<Real> allowed_position;
487 if (quantisation_ != TEST)
489 if (quantisation_ <= NORMAL)
491 if ((multiple_i_ <= 2) || (abs (beamdy_f) >= staffline_f / 2))
492 allowed_position.push (straddle);
493 if ((multiple_i_ <= 1) || (abs (beamdy_f) >= staffline_f / 2))
494 allowed_position.push (sit);
495 allowed_position.push (hang);
498 // TODO: check and fix TRADITIONAL
500 if ((multiple_i_ <= 2) || (abs (beamdy_f) >= staffline_f / 2))
501 allowed_position.push (straddle);
502 if ((multiple_i_ <= 1) && (beamdy_f <= staffline_f / 2))
503 allowed_position.push (sit);
504 if (beamdy_f >= -staffline_f / 2)
505 allowed_position.push (hang);
512 allowed_position.push (hang);
513 cout << "hang" << hang << "\n";
515 else if (test_pos==1)
517 allowed_position.push (straddle);
518 cout << "straddle" << straddle << endl;
520 else if (test_pos==2)
522 allowed_position.push (sit);
523 cout << "sit" << sit << endl;
525 else if (test_pos==3)
527 allowed_position.push (inter);
528 cout << "inter" << inter << endl;
532 Interval iv = quantise_iv (allowed_position, space, dy_f);
534 Real quanty_f = dy_f - iv.min () <= iv.max () - dy_f ? iv.min () : iv.max ();
536 quanty_f = iv.max ();
538 // dim(left_y_) = internote
539 left_y_ = dir_ * quanty_f / internote_f;
543 Beam::set_stemlens ()
545 Real staffline_f = paper_l ()->rule_thickness ();
547 Real epsilon_f = staffline_f / 8;
549 DOUT << "Beam::set_stemlens: \n";
550 Real dy_f = check_stemlengths_f (false);
551 for (int i = 0; i < 2; i++)
553 left_y_ += dy_f * dir_;
554 quantise_left_y (dy_f);
555 dy_f = check_stemlengths_f (true);
556 if (abs (dy_f) <= epsilon_f)
558 DOUT << "Beam::set_stemlens: " << i << " iterations\n";
569 ugh. this is broken and should be rewritten.
573 Beam::set_grouping (Rhythmic_grouping def, Rhythmic_grouping cur)
577 assert (cur.children.size () == stems_.size ());
584 for (int j=0; j <stems_.size (); j++)
588 int f = s->flag_i_ - 2;
593 b= cur.generate_beams (flags, fi);
596 assert (stems_.size () == b.size ()/2);
599 for (int j=0, i=0; i < b.size () && j <stems_.size (); j++)
604 if (s->beams_i_drul_[d] < 0)
605 s->beams_i_drul_[d] = b[i];
607 multiple_i_ = multiple_i_ >? s->beams_i_drul_[d];
609 } while ((flip (&d)) != LEFT);
614 beams to go with one stem.
617 Beam::stem_beams (Stem *here, Stem *next, Stem *prev) const
619 assert (!next || next->hpos_f () > here->hpos_f ());
620 assert (!prev || prev->hpos_f () < here->hpos_f ());
622 Real staffline_f = paper_l ()->rule_thickness ();
623 Real interbeam_f = paper_l ()->interbeam_f (multiple_i_);
625 Real internote_f = here->staff_line_leading_f ()/2;
626 Real beam_f = paper_l ()->beam_thickness_f ();
628 Real dy = interbeam_f;
629 Real stemdx = staffline_f;
630 Real sl = slope_f_* internote_f;
631 lookup_l ()->beam (sl, 20 PT, 1 PT);
637 Real nw_f = paper_l ()->note_width () * 0.8;
639 /* half beams extending to the left. */
642 int lhalfs= lhalfs = here->beams_i_drul_[LEFT] - prev->beams_i_drul_[RIGHT] ;
643 int lwholebeams= here->beams_i_drul_[LEFT] <? prev->beams_i_drul_[RIGHT] ;
645 Half beam should be one note-width,
646 but let's make sure two half-beams never touch
648 Real w = here->hpos_f () - prev->hpos_f ();
651 if (lhalfs) // generates warnings if not
652 a = lookup_l ()->beam (sl, w, beam_f);
653 a.translate (Offset (-w, -w * sl));
654 for (int j = 0; j < lhalfs; j++)
657 b.translate_axis (-dir_ * dy * (lwholebeams+j), Y_AXIS);
658 leftbeams.add_molecule (b);
664 int rhalfs = here->beams_i_drul_[RIGHT] - next->beams_i_drul_[LEFT];
665 int rwholebeams = here->beams_i_drul_[RIGHT] <? next->beams_i_drul_[LEFT];
667 Real w = next->hpos_f () - here->hpos_f ();
668 Molecule a = lookup_l ()->beam (sl, w + stemdx, beam_f);
669 a.translate_axis( - stemdx/2, X_AXIS);
672 if (here->beam_gap_i_)
674 int nogap = rwholebeams - here->beam_gap_i_;
675 for (; j < nogap; j++)
678 b.translate_axis (-dir_ * dy * j, Y_AXIS);
679 rightbeams.add_molecule (b);
681 // TODO: notehead widths differ for different types
684 a = lookup_l ()->beam (sl, w + stemdx, beam_f);
687 for (; j < rwholebeams; j++)
690 b.translate (Offset (gap_f, -dir_ * dy * j));
691 rightbeams.add_molecule (b);
696 a = lookup_l ()->beam (sl, w, beam_f);
698 for (; j < rwholebeams + rhalfs; j++)
701 b.translate_axis (-dir_ * dy * j, Y_AXIS);
702 rightbeams.add_molecule (b);
706 leftbeams.add_molecule (rightbeams);
709 Does beam quanting think of the asymetry of beams?
710 Refpoint is on bottom of symbol. (FIXTHAT) --hwn.