2 beam.cc -- implement Beam
4 source file of the GNU LilyPond music typesetter
6 (c) 1997--1999 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 "chord-tremolo.hh"
28 #include "new-beaming.hh"
30 #include "dimensions.hh"
34 #include "molecule.hh"
35 #include "leastsquares.hh"
37 #include "paper-def.hh"
44 quantisation_ = NORMAL;
49 Beam::add_stem (Stem*s)
54 dim_cache_[Y_AXIS]->parent_l_ = s->dim_cache_[Y_AXIS];
58 s->add_dependency (this);
63 if (!spanned_drul_[LEFT])
70 Beam::get_stem_info (Stem *s)
73 for (int i=0; i < sinfo_.size (); i++)
75 if (sinfo_[i].stem_l_ == s)
83 Beam::do_brew_molecule_p () const
85 Molecule *mol_p = new Molecule;
89 Real x0 = stems_[0]->hpos_f ();
90 for (int j=0; j <stems_.size (); j++)
93 Stem * prev = (j > 0)? stems_[j-1] : 0;
94 Stem * next = (j < stems_.size ()-1) ? stems_[j+1] :0;
96 Molecule sb = stem_beams (i, next, prev);
97 Real x = i->hpos_f ()-x0;
98 sb.translate (Offset (x, (x * slope_f_ + left_y_) *
99 i->staff_line_leading_f ()/2 ));
100 mol_p->add_molecule (sb);
102 mol_p->translate_axis (x0
103 - spanned_drul_[LEFT]->absolute_coordinate (X_AXIS), X_AXIS);
109 Beam::center () const
111 Stem_info si = sinfo_[0];
113 Real w= (si.stem_l_->note_delta_f () + extent (X_AXIS).length ())/2.0;
114 return Offset (w, ( w* slope_f_) *
115 si.stem_l_->staff_line_leading_f ()/2);
119 Beam::do_pre_processing ()
122 dir_ = get_default_dir ();
125 set_direction (dir_);
129 Beam::do_print () const
132 DOUT << "slope_f_ " << slope_f_ << "left ypos " << left_y_;
133 Spanner::do_print ();
138 Beam::do_post_processing ()
140 if (stems_.size () < 2)
142 warning (_ ("beam with less than two stems"));
143 set_elt_property (transparent_scm_sym, SCM_BOOL_T);
151 Beam::do_substitute_element_pointer (Score_element*o,Score_element*n)
153 if (Stem * os = dynamic_cast<Stem*> (o))
154 stems_.substitute (os,
155 dynamic_cast<Stem *> (n));
159 Beam::do_width () const
161 return Interval (stems_[0]->hpos_f (),
162 stems_.top ()->hpos_f ());
166 Beam::get_default_dir () const
168 Drul_array<int> total;
169 total[UP] = total[DOWN] = 0;
170 Drul_array<int> count;
171 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.
198 If dir is not determined: up (see stem::get_default_dir ())
202 Direction neutral_dir = (int)paper_l ()->get_var ("stem_default_neutral_direction");
204 Dir_algorithm a = (Dir_algorithm)rint(paper_l ()->get_var ("beam_dir_algorithm"));
208 beam_dir = (count[UP] == count[DOWN]) ? neutral_dir
209 : (count[UP] > count[DOWN]) ? UP : DOWN;
212 // mean center distance
213 beam_dir = (total[UP] == total[DOWN]) ? neutral_dir
214 : (total[UP] > total[DOWN]) ? UP : DOWN;
218 // median center distance
219 if (!count[DOWN] || !count[UP])
221 beam_dir = (count[UP] == count[DOWN]) ? neutral_dir
222 : (count[UP] > count[DOWN]) ? UP : DOWN;
226 beam_dir = (total[UP] / count[UP] == total[DOWN] / count[DOWN])
228 : (total[UP] / count[UP] > total[DOWN] / count[DOWN]) ? UP : DOWN;
236 Beam::set_direction (Direction d)
239 for (int i=0; i <stems_.size (); i++)
242 s->set_elt_property (beam_dir_scm_sym, gh_int2scm (d));
244 SCM force = s->remove_elt_property (dir_forced_scm_sym);
245 if (force == SCM_BOOL_F)
251 See Documentation/tex/fonts.doc
257 assert (sinfo_.size () > 1);
258 DOUT << "Beam::solve_slope: \n";
261 for (int i=0; i < sinfo_.size (); i++)
263 l.input.push (Offset (sinfo_[i].x_, sinfo_[i].idealy_f_));
265 l.minimise (slope_f_, left_y_);
269 ugh. Naming: this doesn't check, but sets as well.
273 Beam::check_stemlengths_f (bool set_b)
275 Real interbeam_f = paper_l ()->interbeam_f (multiple_i_);
277 Real beam_f = paper_l ()->beam_thickness_f ();
278 Real staffline_f = paper_l ()->rule_thickness ();
279 Real epsilon_f = staffline_f / 8;
281 for (int i=0; i < sinfo_.size (); i++)
283 Real y = sinfo_[i].x_ * slope_f_ + left_y_;
286 if (dir_ != sinfo_[i].dir_)
288 Real internote_f = sinfo_[i].stem_l_->staff_line_leading_f ()/2;
289 y -= dir_ * (beam_f / 2
290 + (sinfo_[i].mult_i_ - 1) * interbeam_f) / internote_f;
291 if (!i && sinfo_[i].stem_l_->staff_symbol_l () !=
292 sinfo_.top ().stem_l_->staff_symbol_l ())
293 y += dir_ * (multiple_i_ - (sinfo_[i].stem_l_->flag_i_ - 2) >? 0)
294 * interbeam_f / internote_f;
298 sinfo_[i].stem_l_->set_stemend (y - sinfo_[i].interstaff_f_);
301 if (y > sinfo_[i].maxy_f_)
302 dy_f = dy_f <? sinfo_[i].maxy_f_ - y;
303 if (y < sinfo_[i].miny_f_)
305 // when all too short, normal stems win..
306 if (dy_f < -epsilon_f)
307 warning (_ ("weird beam shift, check your knees"));
308 dy_f = dy_f >? sinfo_[i].miny_f_ - y;
315 Beam::set_steminfo ()
320 assert (multiple_i_);
321 int total_count_i = 0;
322 int forced_count_i = 0;
323 for (int i=0; i < stems_.size (); i++)
327 s->set_default_extents ();
328 if (s->invisible_b ())
330 if (((int)s->chord_start_f ()) && (s->dir_ != s->get_default_dir ()))
335 bool grace_b = get_elt_property (grace_scm_sym) != SCM_BOOL_F;
336 String type_str = grace_b ? "grace_" : "";
337 int stem_max = (int)rint(paper_l ()->get_var ("stem_max"));
338 Real shorten_f = paper_l ()->get_var (type_str + "forced_stem_shorten"
339 + to_str (multiple_i_ <? stem_max));
342 for (int i=0; i < stems_.size (); i++)
346 Chord tremolo needs to beam over invisible stems of wholes
348 if (!dynamic_cast<Chord_tremolo*> (this))
350 if (s->invisible_b ())
354 Stem_info info (s, multiple_i_);
358 if (info.dir_ == dir_)
360 if (forced_count_i == total_count_i)
361 info.idealy_f_ -= shorten_f;
362 else if (forced_count_i > total_count_i / 2)
363 info.idealy_f_ -= shorten_f / 2;
370 Beam::calculate_slope ()
374 slope_f_ = left_y_ = 0;
375 else if (sinfo_[0].idealy_f_ == sinfo_.top ().idealy_f_)
378 left_y_ = sinfo_[0].idealy_f_;
384 Real solved_slope_f = slope_f_;
387 steep slope running against lengthened stem is suspect
389 Real dx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
391 // urg, these y internote-y-dimensions
392 Real internote_f = stems_[0]->staff_line_leading_f ()/2;
394 Real lengthened = paper_l ()->get_var ("beam_lengthened") / internote_f;
395 Real steep = paper_l ()->get_var ("beam_steep_slope") / internote_f;
396 if (((left_y_ - sinfo_[0].idealy_f_ > lengthened)
397 && (slope_f_ > steep))
398 || ((left_y_ + slope_f_ * dx_f - sinfo_.top ().idealy_f_ > lengthened)
399 && (slope_f_ < -steep)))
405 This neat trick is by Werner Lemberg,
406 damped = tanh (slope_f_)
407 corresponds with some tables in [Wanske]
409 SCM damp = remove_elt_property (damping_scm_sym);
410 int damping = 1; // ugh.
411 if (damp!= SCM_BOOL_F)
412 damping = gh_int2scm (SCM_CDR(damp));
415 slope_f_ = 0.6 * tanh (slope_f_) / damping;
419 Real damped_slope_dy_f = (solved_slope_f - slope_f_) * dx_f / 2;
420 left_y_ += damped_slope_dy_f;
431 [Ross] (simplification of)
432 Try to set slope_f_ complying with y-span of:
434 - beam_f / 2 + staffline_f / 2
435 - beam_f + staffline_f
439 if (quantisation_ <= NONE)
442 Real interline_f = stems_[0]->staff_line_leading_f ();
443 Real internote_f = interline_f / 2;
444 Real staffline_f = paper_l ()->rule_thickness ();
445 Real beam_f = paper_l ()->beam_thickness_f ();
447 Real dx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
449 // dim(y) = internote; so slope = (y/internote)/x
450 Real dy_f = dx_f * abs (slope_f_ * internote_f);
454 /* UGR. ICE in 2.8.1; bugreport filed. */
455 Array<Real> allowed_fraction (3);
456 allowed_fraction[0] = 0;
457 allowed_fraction[1] = (beam_f / 2 + staffline_f / 2);
458 allowed_fraction[2] = (beam_f + staffline_f);
461 Interval iv = quantise_iv (allowed_fraction, interline_f, dy_f);
462 quanty_f = (dy_f - iv[SMALLER] <= iv[BIGGER] - dy_f)
467 slope_f_ = (quanty_f / dx_f) / internote_f * sign (slope_f_);
470 static int test_pos = 0;
475 Prevent interference from stafflines and beams. See Documentation/tex/fonts.doc
479 Beam::quantise_left_y (bool extend_b)
482 we only need to quantise the start of the beam as dy is quantised too
483 if extend_b then stems must *not* get shorter
486 if (quantisation_ <= NONE)
490 ----------------------------------------------------------
494 --------------########------------------------------------
497 hang straddle sit inter hang
500 Real space = stems_[0]->staff_line_leading_f ();
501 Real internote_f = space /2;
502 Real staffline_f = paper_l ()->rule_thickness ();
503 Real beam_f = paper_l ()->beam_thickness_f ();
507 it would be nice to have all allowed positions in a runtime matrix:
508 (multiplicity, minimum_beam_dy, maximum_beam_dy)
512 Real sit = beam_f / 2 - staffline_f / 2;
513 Real inter = space / 2;
514 Real hang = space - beam_f / 2 + staffline_f / 2;
517 Put all allowed positions into an array.
518 Whether a position is allowed or not depends on
519 strictness of quantisation, multiplicity and direction.
521 For simplicity, we'll assume dir = UP and correct if
522 dir = DOWN afterwards.
525 // dim(left_y_) = internote
526 Real dy_f = dir_ * left_y_ * internote_f;
528 Real beamdx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
529 Real beamdy_f = beamdx_f * slope_f_ * internote_f;
531 Array<Real> allowed_position;
532 if (quantisation_ != TEST)
534 if (quantisation_ <= NORMAL)
536 if ((multiple_i_ <= 2) || (abs (beamdy_f) >= staffline_f / 2))
537 allowed_position.push (straddle);
538 if ((multiple_i_ <= 1) || (abs (beamdy_f) >= staffline_f / 2))
539 allowed_position.push (sit);
540 allowed_position.push (hang);
543 // TODO: check and fix TRADITIONAL
545 if ((multiple_i_ <= 2) || (abs (beamdy_f) >= staffline_f / 2))
546 allowed_position.push (straddle);
547 if ((multiple_i_ <= 1) && (beamdy_f <= staffline_f / 2))
548 allowed_position.push (sit);
549 if (beamdy_f >= -staffline_f / 2)
550 allowed_position.push (hang);
557 allowed_position.push (hang);
558 cout << "hang" << hang << "\n";
560 else if (test_pos==1)
562 allowed_position.push (straddle);
563 cout << "straddle" << straddle << endl;
565 else if (test_pos==2)
567 allowed_position.push (sit);
568 cout << "sit" << sit << endl;
570 else if (test_pos==3)
572 allowed_position.push (inter);
573 cout << "inter" << inter << endl;
577 Interval iv = quantise_iv (allowed_position, space, dy_f);
579 Real quanty_f = dy_f - iv[SMALLER] <= iv[BIGGER] - dy_f ? iv[SMALLER] : iv[BIGGER];
581 quanty_f = iv[BIGGER];
583 // dim(left_y_) = internote
584 left_y_ = dir_ * quanty_f / internote_f;
588 Beam::set_stemlens ()
590 Real staffline_f = paper_l ()->rule_thickness ();
592 Real epsilon_f = staffline_f / 8;
595 // je bent zelf eng --hwn.
596 Real dy_f = check_stemlengths_f (false);
597 for (int i = 0; i < 2; i++)
599 left_y_ += dy_f * dir_;
600 quantise_left_y (dy_f);
601 dy_f = check_stemlengths_f (true);
602 if (abs (dy_f) <= epsilon_f)
613 Beam::set_beaming (Beaming_info_list *beaming)
616 for (int i=0; i < stems_.size (); i++)
620 if (stems_[i]->beams_i_drul_[d] < 0)
621 stems_[i]->beams_i_drul_[d] = beaming->infos_.elem (i).beams_i_drul_[d];
623 while (flip (&d) != LEFT);
629 Beam::do_add_processing ()
631 for (int i=0; i < stems_.size () ; i++)
635 multiple_i_ = multiple_i_ >? stems_[i]->beams_i_drul_[d];
636 } while ((flip (&d)) != LEFT);
641 stems_[0]->beams_i_drul_[LEFT] =0;
642 stems_.top()->beams_i_drul_[RIGHT] =0;
649 beams to go with one stem.
652 Beam::stem_beams (Stem *here, Stem *next, Stem *prev) const
654 if ((next && !(next->hpos_f () > here->hpos_f ())) ||
655 (prev && !(prev->hpos_f () < here->hpos_f ())))
656 programming_error ("Beams are not left-to-right");
658 Real staffline_f = paper_l ()->rule_thickness ();
659 Real interbeam_f = paper_l ()->interbeam_f (multiple_i_);
661 Real internote_f = here->staff_line_leading_f ()/2;
662 Real beam_f = paper_l ()->beam_thickness_f ();
664 Real dy = interbeam_f;
665 Real stemdx = staffline_f;
666 Real sl = slope_f_* internote_f;
667 lookup_l ()->beam (sl, 20 PT, 1 PT);
674 if (!here->head_l_arr_.size ())
676 else if (here->type_i ()== 1)
677 nw_f = paper_l ()->get_var ("wholewidth");
678 else if (here->type_i () == 2)
679 nw_f = paper_l ()->note_width () * 0.8;
681 nw_f = paper_l ()->get_var ("quartwidth");
683 /* half beams extending to the left. */
686 int lhalfs= lhalfs = here->beams_i_drul_[LEFT] - prev->beams_i_drul_[RIGHT] ;
687 int lwholebeams= here->beams_i_drul_[LEFT] <? prev->beams_i_drul_[RIGHT] ;
689 Half beam should be one note-width,
690 but let's make sure two half-beams never touch
692 Real w = here->hpos_f () - prev->hpos_f ();
695 if (lhalfs) // generates warnings if not
696 a = lookup_l ()->beam (sl, w, beam_f);
697 a.translate (Offset (-w, -w * sl));
698 for (int j = 0; j < lhalfs; j++)
701 b.translate_axis (-dir_ * dy * (lwholebeams+j), Y_AXIS);
702 leftbeams.add_molecule (b);
708 int rhalfs = here->beams_i_drul_[RIGHT] - next->beams_i_drul_[LEFT];
709 int rwholebeams = here->beams_i_drul_[RIGHT] <? next->beams_i_drul_[LEFT];
711 Real w = next->hpos_f () - here->hpos_f ();
712 Molecule a = lookup_l ()->beam (sl, w + stemdx, beam_f);
713 a.translate_axis( - stemdx/2, X_AXIS);
717 SCM gap = get_elt_property (beam_gap_scm_sym);
718 if (gap != SCM_BOOL_F)
720 int gap_i = gh_scm2int (SCM_CDR (gap));
721 int nogap = rwholebeams - gap_i;
723 for (; j < nogap; j++)
726 b.translate_axis (-dir_ * dy * j, Y_AXIS);
727 rightbeams.add_molecule (b);
729 // TODO: notehead widths differ for different types
732 a = lookup_l ()->beam (sl, w + stemdx, beam_f);
735 for (; j < rwholebeams; j++)
738 if (!here->invisible_b ())
739 b.translate (Offset (gap_f, -dir_ * dy * j));
741 b.translate (Offset (0, -dir_ * dy * j));
742 rightbeams.add_molecule (b);
747 a = lookup_l ()->beam (sl, w, beam_f);
749 for (; j < rwholebeams + rhalfs; j++)
752 b.translate_axis (-dir_ * dy * j, Y_AXIS);
753 rightbeams.add_molecule (b);
757 leftbeams.add_molecule (rightbeams);
760 Does beam quanting think of the asymetry of beams?
761 Refpoint is on bottom of symbol. (FIXTHAT) --hwn.