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>
15 * move paper vars to scm
22 #include "dimensions.hh"
26 #include "leastsquares.hh"
28 #include "paper-def.hh"
30 #include "group-interface.hh"
31 #include "staff-symbol-referencer.hh"
32 #include "cross-staff.hh"
36 Group_interface g (this, "stems");
41 Beam::add_stem (Stem*s)
43 Group_interface gi (this, "stems");
46 s->add_dependency (this);
48 assert (!s->beam_l ());
49 s->set_elt_property ("beam", self_scm_);
51 if (!spanned_drul_[LEFT])
58 Beam::get_multiplicity () const
61 for (SCM s = get_elt_property ("stems"); gh_pair_p (s); s = gh_cdr (s))
63 Score_element * sc = unsmob_element (gh_car (s));
65 if (Stem * st = dynamic_cast<Stem*> (sc))
66 m = m >? st->beam_count (LEFT) >? st->beam_count (RIGHT);
72 After pre-processing all directions should be set.
73 Several post-processing routines (stem, slur, script) need stem/beam
75 Currenly, this means that beam has set all stem's directions.
76 [Alternatively, stems could set its own directions, according to
77 their beam, during 'final-pre-processing'.]
80 Beam::do_pre_processing ()
83 if (visible_stem_count () < 2)
85 warning (_ ("beam has less than two stems"));
86 set_elt_property ("transparent", SCM_BOOL_T);
89 if (!get_direction ())
90 set_direction (calc_default_dir ());
93 set_stem_directions ();
102 Beam::calc_default_dir () const
104 Drul_array<int> total;
105 total[UP] = total[DOWN] = 0;
106 Drul_array<int> count;
107 count[UP] = count[DOWN] = 0;
110 for (int i=0; i <stem_count (); i++)
111 do { // HUH -- waar slaat dit op?
113 int current = s->get_direction ()
114 ? (1 + d * s->get_direction ())/2
115 : s->get_center_distance ((Direction)-d);
123 } while (flip(&d) != DOWN);
126 [Ross] states that the majority of the notes dictates the
127 direction (and not the mean of "center distance")
129 But is that because it really looks better, or because he wants
130 to provide some real simple hands-on rules?
132 We have our doubts, so we simply provide all sensible alternatives.
134 If dir is not determined: up (see stem::get_default_dir ()) */
136 Direction beam_dir = CENTER;
137 Direction neutral_dir = (Direction)(int)paper_l ()->get_var ("stem_default_neutral_direction");
139 SCM a = get_elt_property ("beam-dir-algorithm");
141 if (a == ly_symbol2scm ("majority")) // should get default from paper.
142 beam_dir = (count[UP] == count[DOWN]) ? neutral_dir
143 : (count[UP] > count[DOWN]) ? UP : DOWN;
144 else if (a == ly_symbol2scm ("mean"))
145 // mean center distance
146 beam_dir = (total[UP] == total[DOWN]) ? neutral_dir
147 : (total[UP] > total[DOWN]) ? UP : DOWN;
148 else if (a == ly_symbol2scm ("median"))
150 // median center distance
151 if (count[DOWN] && count[UP])
153 beam_dir = (total[UP] / count[UP] == total[DOWN] / count[DOWN])
155 : (total[UP] / count[UP] > total[DOWN] / count[DOWN]) ? UP : DOWN;
159 beam_dir = (count[UP] == count[DOWN]) ? neutral_dir
160 : (count[UP] > count[DOWN]) ? UP : DOWN;
169 Set all stems with non-forced direction to beam direction.
170 Urg: non-forced should become `without/with unforced' direction,
171 once stem gets cleaned-up.
174 Beam::set_stem_directions ()
176 Direction d = get_direction ();
177 for (int i=0; i <stem_count (); i++)
180 SCM force = s->remove_elt_property ("dir-forced");
181 if (force == SCM_UNDEFINED)
182 s->set_direction (d);
189 if (!auto_knee ("auto-interstaff-knee-gap", true))
190 auto_knee ("auto-knee-gap", false);
194 Simplistic auto-knees; only consider vertical gap between two
197 `Forced' stem directions are ignored. If you don't want auto-knees,
198 don't set, or unset autoKneeGap/autoInterstaffKneeGap.
201 Beam::auto_knee (String gap_str, bool interstaff_b)
205 SCM gap = get_elt_property (gap_str);
206 if (gap != SCM_UNDEFINED)
208 int auto_gap_i = gh_scm2int (gap);
209 for (int i=1; i < stem_count (); i++)
211 bool is_b = (bool)(calc_interstaff_dist (stem (i), this)
212 - calc_interstaff_dist (stem (i-1), this));
213 int l_y = (int)(stem (i-1)->chord_start_f ())
214 + (int)calc_interstaff_dist (stem (i-1), this);
215 int r_y = (int)(stem (i)->chord_start_f ())
216 + (int)calc_interstaff_dist (stem (i), this);
217 int gap_i = r_y - l_y;
219 if ((abs (gap_i) >= auto_gap_i) && (!interstaff_b || is_b))
221 knee_y = (r_y + l_y) / 2;
229 for (int i=0; i < stem_count (); i++)
231 int y = (int)(stem (i)->chord_start_f ())
232 + (int)calc_interstaff_dist (stem (i), this);
233 stem (i)->set_direction (y < knee_y ? UP : DOWN);
234 stem (i)->set_elt_property ("dir-forced", SCM_BOOL_T);
241 Set stem's shorten property if unset.
242 TODO: take some y-position (nearest?) into account
245 Beam::set_stem_shorten ()
247 if (!visible_stem_count ())
250 Real forced_fraction = forced_stem_count () / visible_stem_count ();
251 if (forced_fraction < 0.5)
254 int multiplicity = get_multiplicity ();
255 SCM shorten = scm_eval (scm_listify (
256 ly_symbol2scm ("beamed-stem-shorten"),
257 gh_int2scm (multiplicity),
259 Real shorten_f = gh_scm2double (shorten)
260 * Staff_symbol_referencer_interface (this).staff_line_leading_f ();
262 /* cute, but who invented this -- how to customise ? */
263 if (forced_fraction < 1)
266 for (int i=0; i < stem_count (); i++)
269 if (s->invisible_b ())
271 if (s->get_elt_property ("shorten") == SCM_UNDEFINED)
272 s->set_elt_property ("shorten", gh_double2scm (shorten_f));
276 Set elt properties height and y-position if not set.
277 Adjust stem lengths to reach beam.
280 Beam::do_post_processing ()
282 /* first, calculate y, dy */
284 calc_position_and_height (&y, &dy);
285 if (suspect_slope_b (y, dy))
288 Real damped_dy = calc_slope_damping_f (dy);
289 Real quantised_dy = quantise_dy_f (damped_dy);
291 y += (dy - quantised_dy) / 2;
295 until here, we used only stem_info, which acts as if dir=up
297 y *= get_direction ();
298 dy *= get_direction ();
300 /* set or read dy as necessary */
301 SCM s = get_elt_property ("height");
302 if (s != SCM_UNDEFINED)
303 dy = gh_scm2double (s);
305 set_elt_property ("height", gh_double2scm (dy));
307 /* set or read y as necessary */
308 s = get_elt_property ("y-position");
309 if (s != SCM_UNDEFINED)
311 y = gh_scm2double (s);
312 set_stem_length (y, dy);
316 /* we can modify y, so we should quantise y */
317 Real y_shift = check_stem_length_f (y, dy);
319 y = quantise_y_f (y, dy, 0);
320 set_stem_length (y, dy);
321 y_shift = check_stem_length_f (y, dy);
323 Real internote_f = paper_l ()->get_var ("interline") / 2;
324 if (y_shift > internote_f / 4)
329 for significantly lengthened or shortened stems,
330 request quanting the other way.
333 if (abs (y_shift) > internote_f / 2)
334 quant_dir = sign (y_shift) * get_direction ();
335 y = quantise_y_f (y, dy, quant_dir);
336 set_stem_length (y, dy);
339 set_elt_property ("y-position", gh_double2scm (y));
344 See Documentation/tex/fonts.doc
347 Beam::calc_position_and_height (Real* y, Real* dy) const
350 if (visible_stem_count () <= 1)
353 Real first_ideal = first_visible_stem ()->calc_stem_info ().idealy_f_;
354 if (first_ideal == last_visible_stem ()->calc_stem_info ().idealy_f_)
362 Real x0 = first_visible_stem ()->hpos_f ();
363 for (int i=0; i < stem_count (); i++)
366 if (s->invisible_b ())
368 ls.input.push (Offset (s->hpos_f () - x0,
369 s->calc_stem_info ().idealy_f_));
372 ls.minimise (dydx, *y); // duh, takes references
374 Real dx = last_visible_stem ()->hpos_f () - x0;
379 Beam::suspect_slope_b (Real y, Real dy) const
382 steep slope running against lengthened stem is suspect
384 Real first_ideal = first_visible_stem ()->calc_stem_info ().idealy_f_;
385 Real last_ideal = last_visible_stem ()->calc_stem_info ().idealy_f_;
386 Real lengthened = paper_l ()->get_var ("beam_lengthened");
387 Real steep = paper_l ()->get_var ("beam_steep_slope");
389 Real dx = last_visible_stem ()->hpos_f () - first_visible_stem ()->hpos_f ();
392 if (((y - first_ideal > lengthened) && (dydx > steep))
393 || ((y + dy - last_ideal > lengthened) && (dydx < -steep)))
401 This neat trick is by Werner Lemberg,
402 damped = tanh (slope)
403 corresponds with some tables in [Wanske]
406 Beam::calc_slope_damping_f (Real dy) const
408 SCM damp = get_elt_property ("damping"); // remove?
409 int damping = 1; // ugh.
410 if (damp != SCM_UNDEFINED)
411 damping = gh_scm2int (damp);
415 Real dx = last_visible_stem ()->hpos_f ()
416 - first_visible_stem ()->hpos_f ();
418 dydx = 0.6 * tanh (dydx) / damping;
425 Beam::calc_stem_y_f (Stem* s, Real y, Real dy) const
427 Real beam_f = gh_scm2double (get_elt_property ("beam-thickness"));
428 int multiplicity = get_multiplicity ();
431 Real interbeam_f = paper_l ()->interbeam_f (multiplicity);
432 Real x0 = first_visible_stem ()->hpos_f ();
433 Real dx = last_visible_stem ()->hpos_f () - x0;
434 Real stem_y = (s->hpos_f () - x0) / dx * dy + y;
437 if (get_direction () != s->get_direction ())
439 stem_y -= get_direction () * (beam_f / 2
440 + (multiplicity - 1) * interbeam_f);
442 Staff_symbol_referencer_interface me (s);
443 Staff_symbol_referencer_interface last (last_visible_stem ());
445 if ((s != first_visible_stem ())
446 && me.staff_symbol_l () != last.staff_symbol_l ())
447 stem_y += get_direction ()
448 * (multiplicity - (s->flag_i () - 2) >? 0) * interbeam_f;
454 Beam::check_stem_length_f (Real y, Real dy) const
458 for (int i=0; i < stem_count (); i++)
461 if (s->invisible_b ())
464 Real stem_y = calc_stem_y_f (s, y, dy);
466 stem_y *= get_direction ();
467 Stem_info info = s->calc_stem_info ();
469 if (stem_y > info.maxy_f_)
470 shorten = shorten <? info.maxy_f_ - stem_y;
472 if (stem_y < info.miny_f_)
473 lengthen = lengthen >? info.miny_f_ - stem_y;
476 if (lengthen && shorten)
477 warning (_ ("weird beam vertical offset"));
479 /* when all stems are too short, normal stems win */
481 return shorten * get_direction ();
483 return lengthen * get_direction ();
487 Beam::set_stem_length (Real y, Real dy)
489 Real internote_f = paper_l ()->get_var ("interline") / 2;
490 for (int i=0; i < stem_count (); i++)
493 if (s->invisible_b ())
496 Real stem_y = calc_stem_y_f (s, y, dy);
498 /* caution: stem measures in staff-positions */
499 s->set_stemend ((stem_y - calc_interstaff_dist (s, this)) / internote_f);
504 [Ross] (simplification of)
505 Try to set dy complying with:
507 - beam_f / 2 + staffline_f / 2
508 - beam_f + staffline_f
511 TODO: get allowed-positions as scm list (aarg: from paper block)
514 Beam::quantise_dy_f (Real dy) const
516 SCM s = get_elt_property ("slope-quantisation");
518 if (s == ly_symbol2scm ("none"))
521 Staff_symbol_referencer_interface st (this);
522 Real interline_f = st.staff_line_leading_f ();
524 Real staffline_f = paper_l ()->get_var ("stafflinethickness");
525 Real beam_f = gh_scm2double (get_elt_property ("beam-thickness"));;
527 Array<Real> allowed_fraction (3);
528 allowed_fraction[0] = 0;
529 allowed_fraction[1] = (beam_f / 2 + staffline_f / 2);
530 allowed_fraction[2] = (beam_f + staffline_f);
532 Interval iv = quantise_iv (allowed_fraction, interline_f, abs (dy));
533 Real q = (abs (dy) - iv[SMALLER] <= iv[BIGGER] - abs (dy))
537 return q * sign (dy);
541 Prevent interference from stafflines and beams.
542 See Documentation/tex/fonts.doc
544 TODO: get allowed-positions as scm list (aarg: from paper block)
547 Beam::quantise_y_f (Real y, Real dy, int quant_dir)
550 We only need to quantise the (left) y-position of the beam,
551 since dy is quantised too.
552 if extend_b then stems must *not* get shorter
554 SCM s = get_elt_property ("slope-quantisation");
555 if (s == ly_symbol2scm ("none"))
559 ----------------------------------------------------------
563 --------------########------------------------------------
566 hang straddle sit inter hang
569 Staff_symbol_referencer_interface sinf (this);
570 Real space = sinf.staff_line_leading_f ();
571 Real staffline_f = paper_l ()->get_var ("stafflinethickness");
572 Real beam_f = gh_scm2double (get_elt_property ("beam-thickness"));;
575 Real sit = beam_f / 2 - staffline_f / 2;
576 Real hang = space - beam_f / 2 + staffline_f / 2;
579 Put all allowed positions into an array.
580 Whether a position is allowed or not depends on
581 strictness of quantisation, multiplicity and direction.
583 For simplicity, we'll assume dir = UP and correct if
584 dir = DOWN afterwards.
587 int multiplicity = get_multiplicity ();
590 Array<Real> allowed_position;
591 if (s == ly_symbol2scm ("normal"))
593 if ((multiplicity <= 2) || (abs (dy) >= staffline_f / 2))
594 allowed_position.push (straddle);
595 if ((multiplicity <= 1) || (abs (dy) >= staffline_f / 2))
596 allowed_position.push (sit);
597 allowed_position.push (hang);
599 else if (s == ly_symbol2scm ("traditional"))
601 // TODO: check and fix TRADITIONAL
602 if ((multiplicity <= 2) || (abs (dy) >= staffline_f / 2))
603 allowed_position.push (straddle);
604 if ((multiplicity <= 1) && (dy <= staffline_f / 2))
605 allowed_position.push (sit);
606 if (dy >= -staffline_f / 2)
607 allowed_position.push (hang);
610 Real up_y = get_direction () * y;
611 Interval iv = quantise_iv (allowed_position, space, up_y);
613 Real q = up_y - iv[SMALLER] <= iv[BIGGER] - up_y
614 ? iv[SMALLER] : iv[BIGGER];
616 q = iv[(Direction)quant_dir];
618 return q * get_direction ();
622 Beam::set_beaming (Beaming_info_list *beaming)
625 for (int i=0; i < stem_count (); i++)
629 if (stem (i)->beam_count (d) == 0)
630 stem (i)->set_beaming ( beaming->infos_.elem (i).beams_i_drul_[d],d);
632 while (flip (&d) != LEFT);
639 beams to go with one stem.
645 Beam::stem_beams (Stem *here, Stem *next, Stem *prev) const
647 if ((next && !(next->hpos_f () > here->hpos_f ())) ||
648 (prev && !(prev->hpos_f () < here->hpos_f ())))
649 programming_error ("Beams are not left-to-right");
651 Real staffline_f = paper_l ()->get_var ("stafflinethickness");
652 int multiplicity = get_multiplicity ();
655 Real interbeam_f = paper_l ()->interbeam_f (multiplicity);
656 Real beam_f = gh_scm2double (get_elt_property ("beam-thickness"));;
658 Real dy = interbeam_f;
659 Real stemdx = staffline_f;
661 Real dx = last_visible_stem ()->hpos_f () - first_visible_stem ()->hpos_f ();
662 Real dydx = get_real ("height")/dx;
669 if (!here->first_head ())
671 else if (here->type_i ()== 1)
672 nw_f = paper_l ()->get_var ("wholewidth");
673 else if (here->type_i () == 2)
674 nw_f = paper_l ()->get_var ("notewidth") * 0.8;
676 nw_f = paper_l ()->get_var ("quartwidth");
678 /* half beams extending to the left. */
681 int lhalfs= lhalfs = here->beam_count (LEFT) - prev->beam_count (RIGHT);
682 int lwholebeams= here->beam_count (LEFT) <? prev->beam_count (RIGHT) ;
684 Half beam should be one note-width,
685 but let's make sure two half-beams never touch
687 Real w = here->hpos_f () - prev->hpos_f ();
690 if (lhalfs) // generates warnings if not
691 a = lookup_l ()->beam (dydx, w, beam_f);
692 a.translate (Offset (-w, -w * dydx));
693 for (int j = 0; j < lhalfs; j++)
696 b.translate_axis (-get_direction () * dy * (lwholebeams+j), Y_AXIS);
697 leftbeams.add_molecule (b);
703 int rhalfs = here->beam_count (RIGHT) - next->beam_count (LEFT);
704 int rwholebeams= here->beam_count (RIGHT) <? next->beam_count (LEFT) ;
706 Real w = next->hpos_f () - here->hpos_f ();
707 Molecule a = lookup_l ()->beam (dydx, w + stemdx, beam_f);
708 a.translate_axis( - stemdx/2, X_AXIS);
712 SCM gap = get_elt_property ("beam-gap");
713 if (gap != SCM_UNDEFINED)
715 int gap_i = gh_scm2int ( (gap));
716 int nogap = rwholebeams - gap_i;
718 for (; j < nogap; j++)
721 b.translate_axis (-get_direction () * dy * j, Y_AXIS);
722 rightbeams.add_molecule (b);
724 // TODO: notehead widths differ for different types
727 a = lookup_l ()->beam (dydx, w + stemdx, beam_f);
730 for (; j < rwholebeams; j++)
733 if (!here->invisible_b ())
734 b.translate (Offset (gap_f, -get_direction () * dy * j));
736 b.translate (Offset (0, -get_direction () * dy * j));
737 rightbeams.add_molecule (b);
742 a = lookup_l ()->beam (dydx, w, beam_f);
744 for (; j < rwholebeams + rhalfs; j++)
747 b.translate_axis (-get_direction () * dy * j, Y_AXIS);
748 rightbeams.add_molecule (b);
752 leftbeams.add_molecule (rightbeams);
755 Does beam quanting think of the asymetry of beams?
756 Refpoint is on bottom of symbol. (FIXTHAT) --hwn.
763 Beam::do_brew_molecule_p () const
765 Molecule *mol_p = new Molecule;
769 Real x0 = first_visible_stem ()->hpos_f ();
770 Real dx = last_visible_stem ()->hpos_f () - x0;
771 Real dydx = get_real ("height")/dx;
772 Real y = get_real ("y-position");
773 for (int j=0; j <stem_count (); j++)
776 Stem * prev = (j > 0)? stem (j-1) : 0;
777 Stem * next = (j < stem_count ()-1) ? stem (j+1) :0;
779 Molecule sb = stem_beams (i, next, prev);
780 Real x = i->hpos_f ()-x0;
781 sb.translate (Offset (x, x * dydx + y));
782 mol_p->add_molecule (sb);
784 mol_p->translate_axis (x0
785 - spanned_drul_[LEFT]->relative_coordinate (0, X_AXIS), X_AXIS);
791 Beam::forced_stem_count () const
794 for (int i=0; i < stem_count (); i++)
798 if (s->invisible_b ())
801 if (((int)s->chord_start_f ())
802 && (s->get_direction () != s->get_default_dir ()))
811 TODO: Fix this class. This is wildly inefficient.
812 And it sux. Yet another array/list 'interface'.
815 Beam::stem (int i) const
817 return Group_interface__extract_elements ((Beam*) this, (Stem*) 0, "stems")[i];
821 Beam::stem_count () const
823 Group_interface gi (this, "stems");
828 Beam::stem_top () const
830 SCM s = get_elt_property ("stems");
832 return gh_pair_p (s) ? dynamic_cast<Stem*> (unsmob_element (gh_car (s))) : 0;
834 //Group_interface__extract_elements ((Beam*) this, (Stem*) 0, "stems")[stem_count () - 1];
839 Beam::visible_stem_count () const
842 for (int i = 0; i < stem_count (); i++)
844 if (!stem (i)->invisible_b ())
851 Beam::first_visible_stem () const
853 for (int i = 0; i < stem_count (); i++)
856 if (!s->invisible_b ())
866 Beam::last_visible_stem () const
868 for (int i = stem_count (); i > 0; i--)
870 Stem* s = stem (i - 1);
871 if (!s->invisible_b ())