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.
28 #include "dimensions.hh"
32 #include "molecule.hh"
33 #include "leastsquares.hh"
35 #include "paper-def.hh"
37 #include "rhythmic-grouping.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 Real internote_f = stems_[0]->staff_line_leading_f ()/2;
324 int stem_max = (int)rint(paper_l ()->get_var ("stem_max"));
325 Real shorten_f = paper_l ()->get_var (String ("forced_stem_shorten"
326 + 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;
576 Real dy_f = check_stemlengths_f (false);
577 for (int i = 0; i < 2; i++)
579 left_y_ += dy_f * dir_;
580 quantise_left_y (dy_f);
581 dy_f = check_stemlengths_f (true);
582 if (abs (dy_f) <= epsilon_f)
594 ugh. this is broken and should be rewritten.
598 Beam::set_grouping (Rhythmic_grouping def, Rhythmic_grouping cur)
602 assert (cur.children.size () == stems_.size ());
609 for (int j=0; j <stems_.size (); j++)
613 int f = s->flag_i_ - 2;
618 b= cur.generate_beams (flags, fi);
621 assert (stems_.size () == b.size ()/2);
624 for (int j=0, i=0; i < b.size () && j <stems_.size (); j++)
629 if (s->beams_i_drul_[d] < 0)
630 s->beams_i_drul_[d] = b[i];
632 multiple_i_ = multiple_i_ >? s->beams_i_drul_[d];
634 } while ((flip (&d)) != LEFT);
639 beams to go with one stem.
642 Beam::stem_beams (Stem *here, Stem *next, Stem *prev) const
644 assert (!next || next->hpos_f () > here->hpos_f ());
645 assert (!prev || prev->hpos_f () < here->hpos_f ());
647 Real staffline_f = paper_l ()->rule_thickness ();
648 Real interbeam_f = paper_l ()->interbeam_f (multiple_i_);
650 Real internote_f = here->staff_line_leading_f ()/2;
651 Real beam_f = paper_l ()->beam_thickness_f ();
653 Real dy = interbeam_f;
654 Real stemdx = staffline_f;
655 Real sl = slope_f_* internote_f;
656 lookup_l ()->beam (sl, 20 PT, 1 PT);
662 Real nw_f = paper_l ()->note_width () * 0.8;
664 /* half beams extending to the left. */
667 int lhalfs= lhalfs = here->beams_i_drul_[LEFT] - prev->beams_i_drul_[RIGHT] ;
668 int lwholebeams= here->beams_i_drul_[LEFT] <? prev->beams_i_drul_[RIGHT] ;
670 Half beam should be one note-width,
671 but let's make sure two half-beams never touch
673 Real w = here->hpos_f () - prev->hpos_f ();
676 if (lhalfs) // generates warnings if not
677 a = lookup_l ()->beam (sl, w, beam_f);
678 a.translate (Offset (-w, -w * sl));
679 for (int j = 0; j < lhalfs; j++)
682 b.translate_axis (-dir_ * dy * (lwholebeams+j), Y_AXIS);
683 leftbeams.add_molecule (b);
689 int rhalfs = here->beams_i_drul_[RIGHT] - next->beams_i_drul_[LEFT];
690 int rwholebeams = here->beams_i_drul_[RIGHT] <? next->beams_i_drul_[LEFT];
692 Real w = next->hpos_f () - here->hpos_f ();
693 Molecule a = lookup_l ()->beam (sl, w + stemdx, beam_f);
694 a.translate_axis( - stemdx/2, X_AXIS);
698 SCM gap = get_elt_property (beam_gap_scm_sym);
699 if (gap != SCM_BOOL_F)
701 int gap_i = gh_scm2int (gap);
702 int nogap = rwholebeams - gap_i;
704 for (; j < nogap; j++)
707 b.translate_axis (-dir_ * dy * j, Y_AXIS);
708 rightbeams.add_molecule (b);
710 // TODO: notehead widths differ for different types
713 a = lookup_l ()->beam (sl, w + stemdx, beam_f);
716 for (; j < rwholebeams; j++)
719 b.translate (Offset (gap_f, -dir_ * dy * j));
720 rightbeams.add_molecule (b);
725 a = lookup_l ()->beam (sl, w, beam_f);
727 for (; j < rwholebeams + rhalfs; j++)
730 b.translate_axis (-dir_ * dy * j, Y_AXIS);
731 rightbeams.add_molecule (b);
735 leftbeams.add_molecule (rightbeams);
738 Does beam quanting think of the asymetry of beams?
739 Refpoint is on bottom of symbol. (FIXTHAT) --hwn.