lilypond-1.3.7
[lilypond.git] / lily / beam.cc
blobc489e8f6e74364d5a9d1bfec89c8936348381da4
1 /*
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>
9 */
13 [TODO]
14 * center beam symbol
15 * less hairy code
16 * redo grouping
18 TODO:
20 The relationship Stem <-> Beam is way too hairy. Let's figure who
21 needs what, and what information should be available when.
25 #include <math.h>
27 #include "beaming.hh"
28 #include "proto.hh"
29 #include "dimensions.hh"
30 #include "beam.hh"
31 #include "misc.hh"
32 #include "debug.hh"
33 #include "molecule.hh"
34 #include "leastsquares.hh"
35 #include "stem.hh"
36 #include "paper-def.hh"
37 #include "lookup.hh"
39 Beam::Beam ()
41 slope_f_ = 0;
42 left_y_ = 0;
43 multiple_i_ = 0;
46 void
47 Beam::add_stem (Stem*s)
49 #if 0
51 should figure out why this didn't work.
53 --hwn.
55 if (!stems_.size ())
57 set_parent (s, Y_AXIS);
59 #endif
60 stems_.push (s);
61 s->add_dependency (this);
63 assert (!s->beam_l_);
64 s->beam_l_ = this;
66 if (!spanned_drul_[LEFT])
67 set_bounds (LEFT,s);
68 else
69 set_bounds (RIGHT,s);
72 Stem_info
73 Beam::get_stem_info (Stem *s)
75 Stem_info i;
76 for (int i=0; i < sinfo_.size (); i++)
78 if (sinfo_[i].stem_l_ == s)
79 return sinfo_[i];
81 assert (false);
82 return i;
85 Molecule*
86 Beam::do_brew_molecule_p () const
88 Molecule *mol_p = new Molecule;
89 if (!sinfo_.size ())
90 return mol_p;
92 Real x0 = stems_[0]->hpos_f ();
93 for (int j=0; j <stems_.size (); j++)
95 Stem *i = stems_[j];
96 Stem * prev = (j > 0)? stems_[j-1] : 0;
97 Stem * next = (j < stems_.size ()-1) ? stems_[j+1] :0;
99 Molecule sb = stem_beams (i, next, prev);
100 Real x = i->hpos_f ()-x0;
101 sb.translate (Offset (x, (x * slope_f_ + left_y_) *
102 i->staff_line_leading_f ()/2 ));
103 mol_p->add_molecule (sb);
105 mol_p->translate_axis (x0
106 - spanned_drul_[LEFT]->relative_coordinate (0, X_AXIS), X_AXIS);
108 return mol_p;
111 Offset
112 Beam::center () const
114 Stem_info si = sinfo_[0];
116 Real w= (si.stem_l_->note_delta_f () + extent (X_AXIS).length ())/2.0;
117 return Offset (w, ( w* slope_f_) *
118 si.stem_l_->staff_line_leading_f ()/2);
122 Simplistic auto-knees; only consider vertical gap between two
123 adjacent chords
125 bool
126 Beam::auto_knee (SCM gap, bool interstaff_b)
128 bool knee = false;
129 int knee_y = 0;
130 Real internote_f = stems_[0]->staff_line_leading_f ()/2;
131 if (gap != SCM_UNDEFINED)
133 int auto_gap_i = gh_scm2int (gap);
134 for (int i=1; i < stems_.size (); i++)
136 bool is_b = (bool)(sinfo_[i].interstaff_f_ - sinfo_[i-1].interstaff_f_);
137 int l_y = (int)(stems_[i-1]->chord_start_f () / internote_f)
138 + (int)sinfo_[i-1].interstaff_f_;
139 int r_y = (int)(stems_[i]->chord_start_f () / internote_f)
140 + (int)sinfo_[i].interstaff_f_;
141 int gap_i = r_y - l_y;
144 Forced stem directions are ignored. If you don't want auto-knees,
145 don't set, or unset autoKneeGap/autoInterstaffKneeGap.
147 if ((abs (gap_i) >= auto_gap_i) && (!interstaff_b || is_b))
149 knee_y = (r_y + l_y) / 2;
150 knee = true;
151 break;
155 if (knee)
157 for (int i=0; i < stems_.size (); i++)
159 int y = (int)(stems_[i]->chord_start_f () / internote_f)
160 + (int)sinfo_[i].interstaff_f_;
161 stems_[i]->set_direction ( y < knee_y ? UP : DOWN);
162 stems_[i]->set_elt_property ("dir-forced", SCM_BOOL_T);
165 return knee;
168 bool
169 Beam::auto_knees ()
171 if (auto_knee (get_elt_property ("auto-interstaff-knee-gap"), true))
172 return true;
174 return auto_knee (get_elt_property ("auto-knee-gap"), false);
178 void
179 Beam::do_pre_processing ()
182 urg: it seems that info on whether beam (voice) dir was forced
183 is being junked here?
185 if (!get_direction ())
186 set_direction ( get_default_dir ());
188 set_direction (get_direction ());
191 void
192 Beam::do_print () const
194 #ifndef NPRINT
195 DEBUG_OUT << "slope_f_ " << slope_f_ << "left ypos " << left_y_;
196 Spanner::do_print ();
197 #endif
200 void
201 Beam::do_post_processing ()
203 if (stems_.size () < 2)
205 warning (_ ("beam with less than two stems"));
206 set_elt_property ("transparent", SCM_BOOL_T);
207 return;
209 set_steminfo ();
210 if (auto_knees ())
213 if auto-knee did its work, most probably stem directions
214 have changed, so we must recalculate all.
216 set_direction ( get_default_dir ());
217 set_direction (get_direction ());
219 /* auto-knees used to only work for slope = 0
220 anyway, should be able to set slope per beam
221 set_elt_property ("damping", gh_int2scm(1000));
224 sinfo_.clear ();
225 set_steminfo ();
227 calculate_slope ();
228 set_stemlens ();
231 void
232 Beam::do_substitute_element_pointer (Score_element*o,Score_element*n)
234 if (Stem * os = dynamic_cast<Stem*> (o))
235 stems_.substitute (os,
236 dynamic_cast<Stem *> (n));
239 Interval
240 Beam::do_width () const
242 return Interval (stems_[0]->hpos_f (),
243 stems_.top ()->hpos_f ());
246 Direction
247 Beam::get_default_dir () const
249 Drul_array<int> total;
250 total[UP] = total[DOWN] = 0;
251 Drul_array<int> count;
252 count[UP] = count[DOWN] = 0;
253 Direction d = DOWN;
255 for (int i=0; i <stems_.size (); i++)
256 do {
257 Stem *s = stems_[i];
258 int current = s->get_direction ()
259 ? (1 + d * s->get_direction ())/2
260 : s->get_center_distance ((Direction)-d);
262 if (current)
264 total[d] += current;
265 count[d] ++;
268 } while (flip(&d) != DOWN);
271 [Ross] states that the majority of the notes dictates the
272 direction (and not the mean of "center distance")
274 But is that because it really looks better, or because he wants
275 to provide some real simple hands-on rules?
277 We have our doubts, so we simply provide all sensible alternatives.
279 If dir is not determined: up (see stem::get_default_dir ()) */
281 Direction beam_dir = CENTER;
282 Direction neutral_dir = (Direction)(int)paper_l ()->get_var ("stem_default_neutral_direction");
284 SCM a = get_elt_property ("beam-dir-algorithm");
286 if (a == ly_symbol2scm ("majority")) // should get default from paper.
287 beam_dir = (count[UP] == count[DOWN]) ? neutral_dir
288 : (count[UP] > count[DOWN]) ? UP : DOWN;
289 else if (a == ly_symbol2scm ("mean"))
290 // mean center distance
291 beam_dir = (total[UP] == total[DOWN]) ? neutral_dir
292 : (total[UP] > total[DOWN]) ? UP : DOWN;
293 else if (a == ly_symbol2scm ("median"))
295 // median center distance
296 if (count[DOWN] && count[UP])
298 beam_dir = (total[UP] / count[UP] == total[DOWN] / count[DOWN])
299 ? neutral_dir
300 : (total[UP] / count[UP] > total[DOWN] / count[DOWN]) ? UP : DOWN;
302 else
304 beam_dir = (count[UP] == count[DOWN]) ? neutral_dir
305 : (count[UP] > count[DOWN]) ? UP : DOWN;
309 return beam_dir;
312 void
313 Beam::set_direction (Direction d)
315 Directional_spanner::set_direction (d);
316 for (int i=0; i <stems_.size (); i++)
318 Stem *s = stems_[i];
319 s->set_elt_property ("beam-dir", gh_int2scm (d));
321 SCM force = s->remove_elt_property ("dir-forced");
322 if (force == SCM_UNDEFINED)
323 s->set_direction ( d);
328 See Documentation/tex/fonts.doc
331 void
332 Beam::solve_slope ()
334 assert (sinfo_.size () > 1);
336 Least_squares l;
337 for (int i=0; i < sinfo_.size (); i++)
339 l.input.push (Offset (sinfo_[i].x_, sinfo_[i].idealy_f_));
341 l.minimise (slope_f_, left_y_);
345 ugh. Naming: this doesn't check, but sets as well.
348 Real
349 Beam::check_stemlengths_f (bool set_b)
351 Real interbeam_f = paper_l ()->interbeam_f (multiple_i_);
353 Real beam_f = gh_scm2double (get_elt_property ("beam-thickness"));
354 Real staffline_f = paper_l ()-> get_var ("stafflinethickness");
355 Real epsilon_f = staffline_f / 8;
356 Real dy_f = 0.0;
357 for (int i=0; i < sinfo_.size (); i++)
359 Real y = sinfo_[i].x_ * slope_f_ + left_y_;
361 // correct for knee
362 if (get_direction () != sinfo_[i].get_direction ())
364 Real internote_f = sinfo_[i].stem_l_->staff_line_leading_f ()/2;
365 y -= get_direction () * (beam_f / 2
366 + (sinfo_[i].mult_i_ - 1) * interbeam_f) / internote_f;
367 if (!i && sinfo_[i].stem_l_->staff_symbol_l () !=
368 sinfo_.top ().stem_l_->staff_symbol_l ())
369 y += get_direction () * (multiple_i_ - (sinfo_[i].stem_l_->flag_i_ - 2) >? 0)
370 * interbeam_f / internote_f;
373 if (set_b)
374 sinfo_[i].stem_l_->set_stemend (y - sinfo_[i].interstaff_f_);
376 y *= get_direction ();
377 if (y > sinfo_[i].maxy_f_)
378 dy_f = dy_f <? sinfo_[i].maxy_f_ - y;
379 if (y < sinfo_[i].miny_f_)
381 // when all too short, normal stems win..
382 if (dy_f < -epsilon_f)
383 warning (_ ("weird beam vertical offset"));
384 dy_f = dy_f >? sinfo_[i].miny_f_ - y;
387 return dy_f;
390 void
391 Beam::set_steminfo ()
393 if(!stems_.size ())
394 return;
396 assert (multiple_i_);
398 int total_count_i = 0;
399 int forced_count_i = 0;
400 for (int i=0; i < stems_.size (); i++)
402 Stem *s = stems_[i];
404 s->set_default_extents ();
405 if (s->invisible_b ())
406 continue;
407 if (((int)s->chord_start_f ()) && (s->get_direction () != s->get_default_dir ()))
408 forced_count_i++;
409 total_count_i++;
412 bool grace_b = get_elt_property ("grace") == SCM_BOOL_T;
413 String type_str = grace_b ? "grace_" : "";
414 int stem_max = (int)rint(paper_l ()->get_var ("stem_max"));
415 Real shorten_f = paper_l ()->get_var (type_str + "forced_stem_shorten"
416 + to_str (multiple_i_ <? stem_max));
418 Real leftx = 0;
419 for (int i=0; i < stems_.size (); i++)
421 Stem *s = stems_[i];
423 Chord tremolo needs to beam over invisible stems of wholes
425 SCM trem = get_elt_property ("chord-tremolo");
426 if (gh_boolean_p (trem) && gh_scm2bool (trem))
428 if (s->invisible_b ())
429 continue;
432 Stem_info info (s, multiple_i_);
433 if (leftx == 0)
434 leftx = info.x_;
435 info.x_ -= leftx;
436 if (info.get_direction () == get_direction ())
438 if (forced_count_i == total_count_i)
439 info.idealy_f_ -= shorten_f;
440 else if (forced_count_i > total_count_i / 2)
441 info.idealy_f_ -= shorten_f / 2;
443 sinfo_.push (info);
447 void
448 Beam::calculate_slope ()
450 if (!sinfo_.size ())
451 slope_f_ = left_y_ = 0;
452 else if (sinfo_[0].idealy_f_ == sinfo_.top ().idealy_f_)
454 slope_f_ = 0;
455 left_y_ = sinfo_[0].idealy_f_;
456 left_y_ *= get_direction ();
458 else
460 solve_slope ();
461 Real solved_slope_f = slope_f_;
464 steep slope running against lengthened stem is suspect
466 Real dx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
468 // urg, these y internote-y-dimensions
469 Real internote_f = stems_[0]->staff_line_leading_f ()/2;
471 Real lengthened = paper_l ()->get_var ("beam_lengthened") / internote_f;
472 Real steep = paper_l ()->get_var ("beam_steep_slope") / internote_f;
473 if (((left_y_ - sinfo_[0].idealy_f_ > lengthened)
474 && (slope_f_ > steep))
475 || ((left_y_ + slope_f_ * dx_f - sinfo_.top ().idealy_f_ > lengthened)
476 && (slope_f_ < -steep)))
478 slope_f_ = 0;
482 This neat trick is by Werner Lemberg,
483 damped = tanh (slope_f_)
484 corresponds with some tables in [Wanske]
486 SCM damp = remove_elt_property ("damping");
487 int damping = 1; // ugh.
488 if (damp!= SCM_UNDEFINED)
489 damping = gh_int2scm (damp);
491 if (damping)
492 slope_f_ = 0.6 * tanh (slope_f_) / damping;
494 quantise_dy ();
496 Real damped_slope_dy_f = (solved_slope_f - slope_f_) * dx_f / 2;
497 left_y_ += damped_slope_dy_f;
499 left_y_ *= get_direction ();
500 slope_f_ *= get_direction ();
504 void
505 Beam::quantise_dy ()
508 [Ross] (simplification of)
509 Try to set slope_f_ complying with y-span of:
510 - zero
511 - beam_f / 2 + staffline_f / 2
512 - beam_f + staffline_f
513 + n * interline
516 SCM q = get_elt_property ("slope-quantisation");
518 if (q == ly_symbol2scm ("none"))
519 return;
521 Real interline_f = stems_[0]->staff_line_leading_f ();
522 Real internote_f = interline_f / 2;
523 Real staffline_f = paper_l ()->get_var ("stafflinethickness");
524 Real beam_f = gh_scm2double (get_elt_property ("beam-thickness"));;
526 Real dx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
528 // dim(y) = internote; so slope = (y/internote)/x
529 Real dy_f = dx_f * abs (slope_f_ * internote_f);
531 Real quanty_f = 0.0;
533 Array<Real> allowed_fraction (3);
534 allowed_fraction[0] = 0;
535 allowed_fraction[1] = (beam_f / 2 + staffline_f / 2);
536 allowed_fraction[2] = (beam_f + staffline_f);
539 Interval iv = quantise_iv (allowed_fraction, interline_f, dy_f);
540 quanty_f = (dy_f - iv[SMALLER] <= iv[BIGGER] - dy_f)
541 ? iv[SMALLER]
542 : iv[BIGGER];
545 slope_f_ = (quanty_f / dx_f) / internote_f * sign (slope_f_);
550 Prevent interference from stafflines and beams. See Documentation/tex/fonts.doc
553 void
554 Beam::quantise_left_y (bool extend_b)
557 we only need to quantise the start of the beam as dy is quantised too
558 if extend_b then stems must *not* get shorter
560 SCM q = get_elt_property ("slope-quantisation");
564 ----------------------------------------------------------
565 ########
566 ########
567 ########
568 --------------########------------------------------------
569 ########
571 hang straddle sit inter hang
574 Real space = stems_[0]->staff_line_leading_f ();
575 Real internote_f = space /2;
576 Real staffline_f = paper_l ()->get_var ("stafflinethickness");
577 Real beam_f = gh_scm2double (get_elt_property ("beam-thickness"));;
580 [TODO]
581 it would be nice to have all allowed positions in a runtime matrix:
582 (multiplicity, minimum_beam_dy, maximum_beam_dy)
585 Real straddle = 0;
586 Real sit = beam_f / 2 - staffline_f / 2;
587 Real hang = space - beam_f / 2 + staffline_f / 2;
590 Put all allowed positions into an array.
591 Whether a position is allowed or not depends on
592 strictness of quantisation, multiplicity and direction.
594 For simplicity, we'll assume dir = UP and correct if
595 dir = DOWN afterwards.
597 // isn't this asymmetric ? --hwn
599 // dim(left_y_) = internote
600 Real dy_f = get_direction () * left_y_ * internote_f;
602 Real beamdx_f = stems_.top ()->hpos_f () - stems_[0]->hpos_f ();
603 Real beamdy_f = beamdx_f * slope_f_ * internote_f;
605 Array<Real> allowed_position;
606 if (q == ly_symbol2scm ("normal"))
608 if ((multiple_i_ <= 2) || (abs (beamdy_f) >= staffline_f / 2))
609 allowed_position.push (straddle);
610 if ((multiple_i_ <= 1) || (abs (beamdy_f) >= staffline_f / 2))
611 allowed_position.push (sit);
612 allowed_position.push (hang);
614 else if (q == ly_symbol2scm ("traditional"))
616 // TODO: check and fix TRADITIONAL
617 if ((multiple_i_ <= 2) || (abs (beamdy_f) >= staffline_f / 2))
618 allowed_position.push (straddle);
619 if ((multiple_i_ <= 1) && (beamdy_f <= staffline_f / 2))
620 allowed_position.push (sit);
621 if (beamdy_f >= -staffline_f / 2)
622 allowed_position.push (hang);
626 Interval iv = quantise_iv (allowed_position, space, dy_f);
628 Real quanty_f = dy_f - iv[SMALLER] <= iv[BIGGER] - dy_f ? iv[SMALLER] : iv[BIGGER];
629 if (extend_b)
630 quanty_f = iv[BIGGER];
632 // dim(left_y_) = internote
633 left_y_ = get_direction () * quanty_f / internote_f;
636 void
637 Beam::set_stemlens ()
639 Real staffline_f = paper_l ()->get_var ("stafflinethickness");
640 // enge floots
641 Real epsilon_f = staffline_f / 8;
644 // je bent zelf eng --hwn.
645 Real dy_f = check_stemlengths_f (false);
646 for (int i = 0; i < 2; i++) // 2 ?
648 left_y_ += dy_f * get_direction ();
649 quantise_left_y (dy_f);
650 dy_f = check_stemlengths_f (true);
651 if (abs (dy_f) <= epsilon_f)
653 break;
658 void
659 Beam::set_beaming (Beaming_info_list *beaming)
661 Direction d = LEFT;
662 for (int i=0; i < stems_.size (); i++)
666 if (stems_[i]->beams_i_drul_[d] < 0)
667 stems_[i]->beams_i_drul_[d] = beaming->infos_.elem (i).beams_i_drul_[d];
669 while (flip (&d) != LEFT);
674 void
675 Beam::do_add_processing ()
677 for (int i=0; i < stems_.size () ; i++)
679 Direction d = LEFT;
680 do {
681 multiple_i_ = multiple_i_ >? stems_[i]->beams_i_drul_[d];
682 } while ((flip (&d)) != LEFT);
685 if (stems_.size ())
687 stems_[0]->beams_i_drul_[LEFT] =0;
688 stems_.top()->beams_i_drul_[RIGHT] =0;
695 beams to go with one stem.
697 clean me up.
699 Molecule
700 Beam::stem_beams (Stem *here, Stem *next, Stem *prev) const
702 if ((next && !(next->hpos_f () > here->hpos_f ())) ||
703 (prev && !(prev->hpos_f () < here->hpos_f ())))
704 programming_error ("Beams are not left-to-right");
706 Real staffline_f = paper_l ()->get_var ("stafflinethickness");
707 Real interbeam_f = paper_l ()->interbeam_f (multiple_i_);
709 Real internote_f = here->staff_line_leading_f ()/2;
710 Real beam_f = gh_scm2double (get_elt_property ("beam-thickness"));;
712 Real dy = interbeam_f;
713 Real stemdx = staffline_f;
714 Real sl = slope_f_* internote_f;
716 Molecule leftbeams;
717 Molecule rightbeams;
719 // UGH
720 Real nw_f;
721 if (!here->head_l_arr_.size ())
722 nw_f = 0;
723 else if (here->type_i ()== 1)
724 nw_f = paper_l ()->get_var ("wholewidth");
725 else if (here->type_i () == 2)
726 nw_f = paper_l ()->get_var ("notewidth") * 0.8;
727 else
728 nw_f = paper_l ()->get_var ("quartwidth");
730 /* half beams extending to the left. */
731 if (prev)
733 int lhalfs= lhalfs = here->beams_i_drul_[LEFT] - prev->beams_i_drul_[RIGHT] ;
734 int lwholebeams= here->beams_i_drul_[LEFT] <? prev->beams_i_drul_[RIGHT] ;
736 Half beam should be one note-width,
737 but let's make sure two half-beams never touch
739 Real w = here->hpos_f () - prev->hpos_f ();
740 w = w/2 <? nw_f;
741 Molecule a;
742 if (lhalfs) // generates warnings if not
743 a = lookup_l ()->beam (sl, w, beam_f);
744 a.translate (Offset (-w, -w * sl));
745 for (int j = 0; j < lhalfs; j++)
747 Molecule b (a);
748 b.translate_axis (-get_direction () * dy * (lwholebeams+j), Y_AXIS);
749 leftbeams.add_molecule (b);
753 if (next)
755 int rhalfs = here->beams_i_drul_[RIGHT] - next->beams_i_drul_[LEFT];
756 int rwholebeams = here->beams_i_drul_[RIGHT] <? next->beams_i_drul_[LEFT];
758 Real w = next->hpos_f () - here->hpos_f ();
759 Molecule a = lookup_l ()->beam (sl, w + stemdx, beam_f);
760 a.translate_axis( - stemdx/2, X_AXIS);
761 int j = 0;
762 Real gap_f = 0;
764 SCM gap = get_elt_property ("beam-gap");
765 if (gap != SCM_UNDEFINED)
767 int gap_i = gh_scm2int ( (gap));
768 int nogap = rwholebeams - gap_i;
770 for (; j < nogap; j++)
772 Molecule b (a);
773 b.translate_axis (-get_direction () * dy * j, Y_AXIS);
774 rightbeams.add_molecule (b);
776 // TODO: notehead widths differ for different types
777 gap_f = nw_f / 2;
778 w -= 2 * gap_f;
779 a = lookup_l ()->beam (sl, w + stemdx, beam_f);
782 for (; j < rwholebeams; j++)
784 Molecule b (a);
785 if (!here->invisible_b ())
786 b.translate (Offset (gap_f, -get_direction () * dy * j));
787 else
788 b.translate (Offset (0, -get_direction () * dy * j));
789 rightbeams.add_molecule (b);
792 w = w/2 <? nw_f;
793 if (rhalfs)
794 a = lookup_l ()->beam (sl, w, beam_f);
796 for (; j < rwholebeams + rhalfs; j++)
798 Molecule b (a);
799 b.translate_axis (-get_direction () * dy * j, Y_AXIS);
800 rightbeams.add_molecule (b);
804 leftbeams.add_molecule (rightbeams);
807 Does beam quanting think of the asymetry of beams?
808 Refpoint is on bottom of symbol. (FIXTHAT) --hwn.
810 return leftbeams;