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>
20 The relationship Stem <-> Beam is way too hairy. Let's figure who
21 needs what, and what information should be available when.
27 #include "new-beaming.hh"
29 #include "dimensions.hh"
33 #include "molecule.hh"
34 #include "leastsquares.hh"
36 #include "paper-def.hh"
43 quantisation_ = NORMAL;
48 Beam::add_stem (Stem*s)
53 dim_cache_[Y_AXIS]->parent_l_ = s->dim_cache_[Y_AXIS];
57 s->add_dependency (this);
62 if (!spanned_drul_[LEFT])
69 Beam::get_stem_info (Stem *s)
72 for (int i=0; i < sinfo_.size (); i++)
74 if (sinfo_[i].stem_l_ == s)
82 Beam::do_brew_molecule_p () const
84 Molecule *mol_p = new Molecule;
88 Real x0 = stems_[0]->hpos_f ();
89 for (int j=0; j <stems_.size (); j++)
92 Stem * prev = (j > 0)? stems_[j-1] : 0;
93 Stem * next = (j < stems_.size ()-1) ? stems_[j+1] :0;
95 Molecule sb = stem_beams (i, next, prev);
96 Real x = i->hpos_f ()-x0;
97 sb.translate (Offset (x, (x * slope_f_ + left_y_) *
98 i->staff_line_leading_f ()/2 ));
99 mol_p->add_molecule (sb);
101 mol_p->translate_axis (x0
102 - spanned_drul_[LEFT]->absolute_coordinate (X_AXIS), X_AXIS);
108 Beam::center () const
110 Stem_info si = sinfo_[0];
112 Real w= (si.stem_l_->note_delta_f () + extent (X_AXIS).length ())/2.0;
113 return Offset (w, ( w* slope_f_) *
114 si.stem_l_->staff_line_leading_f ()/2);
118 Beam::do_pre_processing ()
121 dir_ = get_default_dir ();
124 set_direction (dir_);
128 Beam::do_print () const
131 DOUT << "slope_f_ " << slope_f_ << "left ypos " << left_y_;
132 Spanner::do_print ();
137 Beam::do_post_processing ()
139 if (stems_.size () < 2)
141 warning (_ ("beam with less than two stems"));
142 set_elt_property (transparent_scm_sym, SCM_BOOL_T);
150 Beam::do_substitute_element_pointer (Score_element*o,Score_element*n)
152 if (Stem * os = dynamic_cast<Stem*> (o))
153 stems_.substitute (os,
154 dynamic_cast<Stem *> (n));
158 Beam::do_width () const
160 return Interval (stems_[0]->hpos_f (),
161 stems_.top ()->hpos_f ());
165 Beam::get_default_dir () const
167 Drul_array<int> total;
168 total[UP] = total[DOWN] = 0;
169 Drul_array<int> count;
170 count[UP] = count[DOWN] = 0;
174 for (int i=0; i <stems_.size (); i++)
177 int current = s->dir_
178 ? (1 + d * s->dir_)/2
179 : s->get_center_distance ((Direction)-d);
187 } while (flip(&d) != DOWN);
190 [Ross] states that the majority of the notes dictates the
191 direction (and not the mean of "center distance")
193 But is that because it really looks better, or because he
194 wants to provide some real simple hands-on rules.
196 We have our doubts, so we simply provide all sensible alternatives.
199 Dir_algorithm a = (Dir_algorithm)rint(paper_l ()->get_var ("beam_dir_algorithm"));
203 beamdir = (count[UP] > count[DOWN]) ? UP : DOWN;
206 // mean center distance
207 beamdir = (total[UP] > total[DOWN]) ? UP : DOWN;
211 // median center distance
214 else if (!count[DOWN])
217 beamdir = (total[UP] / count[UP] > total[DOWN] / count[DOWN]) ? UP : DOWN;
224 Beam::set_direction (Direction d)
227 for (int i=0; i <stems_.size (); i++)
230 s->set_elt_property (beam_dir_scm_sym, gh_int2scm (d));
232 SCM force = s->remove_elt_property (dir_forced_scm_sym);
233 if (force == SCM_BOOL_F)
239 See Documentation/tex/fonts.doc
245 assert (sinfo_.size () > 1);
246 DOUT << "Beam::solve_slope: \n";
249 for (int i=0; i < sinfo_.size (); i++)
251 l.input.push (Offset (sinfo_[i].x_, sinfo_[i].idealy_f_));
253 l.minimise (slope_f_, left_y_);
257 ugh. Naming: this doesn't check, but sets as well.
261 Beam::check_stemlengths_f (bool set_b)
263 Real interbeam_f = paper_l ()->interbeam_f (multiple_i_);
265 Real beam_f = paper_l ()->beam_thickness_f ();
266 Real staffline_f = paper_l ()->rule_thickness ();
267 Real epsilon_f = staffline_f / 8;
269 for (int i=0; i < sinfo_.size (); i++)
271 Real y = sinfo_[i].x_ * slope_f_ + left_y_;
274 if (dir_ != sinfo_[i].dir_)
276 Real internote_f = sinfo_[i].stem_l_->staff_line_leading_f ()/2;
277 y -= dir_ * (beam_f / 2
278 + (sinfo_[i].mult_i_ - 1) * interbeam_f) / internote_f;
279 if (!i && sinfo_[i].stem_l_->staff_symbol_l () !=
280 sinfo_.top ().stem_l_->staff_symbol_l ())
281 y += dir_ * (multiple_i_ - (sinfo_[i].stem_l_->flag_i_ - 2) >? 0)
282 * interbeam_f / internote_f;
286 sinfo_[i].stem_l_->set_stemend (y - sinfo_[i].interstaff_f_);
289 if (y > sinfo_[i].maxy_f_)
290 dy_f = dy_f <? sinfo_[i].maxy_f_ - y;
291 if (y < sinfo_[i].miny_f_)
293 // when all too short, normal stems win..
294 if (dy_f < -epsilon_f)
295 warning (_ ("weird beam shift, check your knees"));
296 dy_f = dy_f >? sinfo_[i].miny_f_ - y;
303 Beam::set_steminfo ()
308 assert (multiple_i_);
309 int total_count_i = 0;
310 int forced_count_i = 0;
311 for (int i=0; i < stems_.size (); i++)
315 s->set_default_extents ();
316 if (s->invisible_b ())
318 if (((int)s->chord_start_f ()) && (s->dir_ != s->get_default_dir ()))
323 bool grace_b = get_elt_property (grace_scm_sym) != SCM_BOOL_F;
324 String type_str = grace_b ? "grace_" : "";
325 int stem_max = (int)rint(paper_l ()->get_var ("stem_max"));
326 Real shorten_f = paper_l ()->get_var (type_str + "forced_stem_shorten"
327 + to_str (multiple_i_ <? stem_max));
330 for (int i=0; i < stems_.size (); i++)
333 if (s->invisible_b ())
336 Stem_info info (s, multiple_i_);
340 if (info.dir_ == dir_)
342 if (forced_count_i == total_count_i)
343 info.idealy_f_ -= shorten_f;
344 else if (forced_count_i > total_count_i / 2)
345 info.idealy_f_ -= shorten_f / 2;
352 Beam::calculate_slope ()
356 slope_f_ = left_y_ = 0;
357 else if (sinfo_[0].idealy_f_ == sinfo_.top ().idealy_f_)
360 left_y_ = sinfo_[0].idealy_f_;
366 Real solved_slope_f = slope_f_;
369 steep slope running against lengthened stem is suspect
371 Real dx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
373 // urg, these y internote-y-dimensions
374 Real internote_f = stems_[0]->staff_line_leading_f ()/2;
376 Real lengthened = paper_l ()->get_var ("beam_lengthened") / internote_f;
377 Real steep = paper_l ()->get_var ("beam_steep_slope") / internote_f;
378 if (((left_y_ - sinfo_[0].idealy_f_ > lengthened)
379 && (slope_f_ > steep))
380 || ((left_y_ + slope_f_ * dx_f - sinfo_.top ().idealy_f_ > lengthened)
381 && (slope_f_ < -steep)))
387 This neat trick is by Werner Lemberg,
388 damped = tanh (slope_f_)
389 corresponds with some tables in [Wanske]
391 SCM damp = remove_elt_property (damping_scm_sym);
392 int damping = 1; // ugh.
393 if (damp!= SCM_BOOL_F)
394 damping = gh_int2scm (SCM_CDR(damp));
397 slope_f_ = 0.6 * tanh (slope_f_) / damping;
401 Real damped_slope_dy_f = (solved_slope_f - slope_f_) * dx_f / 2;
402 left_y_ += damped_slope_dy_f;
413 [Ross] (simplification of)
414 Try to set slope_f_ complying with y-span of:
416 - beam_f / 2 + staffline_f / 2
417 - beam_f + staffline_f
421 if (quantisation_ <= NONE)
424 Real interline_f = stems_[0]->staff_line_leading_f ();
425 Real internote_f = interline_f / 2;
426 Real staffline_f = paper_l ()->rule_thickness ();
427 Real beam_f = paper_l ()->beam_thickness_f ();
429 Real dx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
431 // dim(y) = internote; so slope = (y/internote)/x
432 Real dy_f = dx_f * abs (slope_f_ * internote_f);
436 /* UGR. ICE in 2.8.1; bugreport filed. */
437 Array<Real> allowed_fraction (3);
438 allowed_fraction[0] = 0;
439 allowed_fraction[1] = (beam_f / 2 + staffline_f / 2);
440 allowed_fraction[2] = (beam_f + staffline_f);
443 Interval iv = quantise_iv (allowed_fraction, interline_f, dy_f);
444 quanty_f = (dy_f - iv[SMALLER] <= iv[BIGGER] - dy_f)
449 slope_f_ = (quanty_f / dx_f) / internote_f * sign (slope_f_);
452 static int test_pos = 0;
457 Prevent interference from stafflines and beams. See Documentation/tex/fonts.doc
461 Beam::quantise_left_y (bool extend_b)
464 we only need to quantise the start of the beam as dy is quantised too
465 if extend_b then stems must *not* get shorter
468 if (quantisation_ <= NONE)
472 ----------------------------------------------------------
476 --------------########------------------------------------
479 hang straddle sit inter hang
482 Real space = stems_[0]->staff_line_leading_f ();
483 Real internote_f = space /2;
484 Real staffline_f = paper_l ()->rule_thickness ();
485 Real beam_f = paper_l ()->beam_thickness_f ();
489 it would be nice to have all allowed positions in a runtime matrix:
490 (multiplicity, minimum_beam_dy, maximum_beam_dy)
494 Real sit = beam_f / 2 - staffline_f / 2;
495 Real inter = space / 2;
496 Real hang = space - beam_f / 2 + staffline_f / 2;
499 Put all allowed positions into an array.
500 Whether a position is allowed or not depends on
501 strictness of quantisation, multiplicity and direction.
503 For simplicity, we'll assume dir = UP and correct if
504 dir = DOWN afterwards.
507 // dim(left_y_) = internote
508 Real dy_f = dir_ * left_y_ * internote_f;
510 Real beamdx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
511 Real beamdy_f = beamdx_f * slope_f_ * internote_f;
513 Array<Real> allowed_position;
514 if (quantisation_ != TEST)
516 if (quantisation_ <= NORMAL)
518 if ((multiple_i_ <= 2) || (abs (beamdy_f) >= staffline_f / 2))
519 allowed_position.push (straddle);
520 if ((multiple_i_ <= 1) || (abs (beamdy_f) >= staffline_f / 2))
521 allowed_position.push (sit);
522 allowed_position.push (hang);
525 // TODO: check and fix TRADITIONAL
527 if ((multiple_i_ <= 2) || (abs (beamdy_f) >= staffline_f / 2))
528 allowed_position.push (straddle);
529 if ((multiple_i_ <= 1) && (beamdy_f <= staffline_f / 2))
530 allowed_position.push (sit);
531 if (beamdy_f >= -staffline_f / 2)
532 allowed_position.push (hang);
539 allowed_position.push (hang);
540 cout << "hang" << hang << "\n";
542 else if (test_pos==1)
544 allowed_position.push (straddle);
545 cout << "straddle" << straddle << endl;
547 else if (test_pos==2)
549 allowed_position.push (sit);
550 cout << "sit" << sit << endl;
552 else if (test_pos==3)
554 allowed_position.push (inter);
555 cout << "inter" << inter << endl;
559 Interval iv = quantise_iv (allowed_position, space, dy_f);
561 Real quanty_f = dy_f - iv[SMALLER] <= iv[BIGGER] - dy_f ? iv[SMALLER] : iv[BIGGER];
563 quanty_f = iv[BIGGER];
565 // dim(left_y_) = internote
566 left_y_ = dir_ * quanty_f / internote_f;
570 Beam::set_stemlens ()
572 Real staffline_f = paper_l ()->rule_thickness ();
574 Real epsilon_f = staffline_f / 8;
577 // je bent zelf eng --hwn.
578 Real dy_f = check_stemlengths_f (false);
579 for (int i = 0; i < 2; i++)
581 left_y_ += dy_f * dir_;
582 quantise_left_y (dy_f);
583 dy_f = check_stemlengths_f (true);
584 if (abs (dy_f) <= epsilon_f)
595 Beam::set_beaming (Beaming_info_list *beaming)
598 for (int i=0; i < stems_.size (); i++)
602 if (stems_[i]->beams_i_drul_[d] < 0)
603 stems_[i]->beams_i_drul_[d] = beaming->infos_.elem (i).beams_i_drul_[d];
605 while (flip (&d) != LEFT);
611 Beam::do_add_processing ()
613 for (int i=0; i < stems_.size () ; i++)
617 multiple_i_ = multiple_i_ >? stems_[i]->beams_i_drul_[d];
618 } while ((flip (&d)) != LEFT);
623 stems_[0]->beams_i_drul_[LEFT] =0;
624 stems_.top()->beams_i_drul_[RIGHT] =0;
631 beams to go with one stem.
634 Beam::stem_beams (Stem *here, Stem *next, Stem *prev) const
636 if ((next && !(next->hpos_f () > here->hpos_f ())) ||
637 (prev && !(prev->hpos_f () < here->hpos_f ())))
638 programming_error ("Beams are not left-to-right");
640 Real staffline_f = paper_l ()->rule_thickness ();
641 Real interbeam_f = paper_l ()->interbeam_f (multiple_i_);
643 Real internote_f = here->staff_line_leading_f ()/2;
644 Real beam_f = paper_l ()->beam_thickness_f ();
646 Real dy = interbeam_f;
647 Real stemdx = staffline_f;
648 Real sl = slope_f_* internote_f;
649 lookup_l ()->beam (sl, 20 PT, 1 PT);
655 Real nw_f = paper_l ()->note_width () * 0.8;
657 /* half beams extending to the left. */
660 int lhalfs= lhalfs = here->beams_i_drul_[LEFT] - prev->beams_i_drul_[RIGHT] ;
661 int lwholebeams= here->beams_i_drul_[LEFT] <? prev->beams_i_drul_[RIGHT] ;
663 Half beam should be one note-width,
664 but let's make sure two half-beams never touch
666 Real w = here->hpos_f () - prev->hpos_f ();
669 if (lhalfs) // generates warnings if not
670 a = lookup_l ()->beam (sl, w, beam_f);
671 a.translate (Offset (-w, -w * sl));
672 for (int j = 0; j < lhalfs; j++)
675 b.translate_axis (-dir_ * dy * (lwholebeams+j), Y_AXIS);
676 leftbeams.add_molecule (b);
682 int rhalfs = here->beams_i_drul_[RIGHT] - next->beams_i_drul_[LEFT];
683 int rwholebeams = here->beams_i_drul_[RIGHT] <? next->beams_i_drul_[LEFT];
685 Real w = next->hpos_f () - here->hpos_f ();
686 Molecule a = lookup_l ()->beam (sl, w + stemdx, beam_f);
687 a.translate_axis( - stemdx/2, X_AXIS);
691 SCM gap = get_elt_property (beam_gap_scm_sym);
692 if (gap != SCM_BOOL_F)
694 int gap_i = gh_scm2int (gap);
695 int nogap = rwholebeams - gap_i;
697 for (; j < nogap; j++)
700 b.translate_axis (-dir_ * dy * j, Y_AXIS);
701 rightbeams.add_molecule (b);
703 // TODO: notehead widths differ for different types
706 a = lookup_l ()->beam (sl, w + stemdx, beam_f);
709 for (; j < rwholebeams; j++)
712 b.translate (Offset (gap_f, -dir_ * dy * j));
713 rightbeams.add_molecule (b);
718 a = lookup_l ()->beam (sl, w, beam_f);
720 for (; j < rwholebeams + rhalfs; j++)
723 b.translate_axis (-dir_ * dy * j, Y_AXIS);
724 rightbeams.add_molecule (b);
728 leftbeams.add_molecule (rightbeams);
731 Does beam quanting think of the asymetry of beams?
732 Refpoint is on bottom of symbol. (FIXTHAT) --hwn.