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>
14 * move paper vars to scm
19 #include "dimensions.hh"
23 #include "leastsquares.hh"
25 #include "paper-def.hh"
27 #include "group-interface.hh"
28 #include "staff-symbol-referencer.hh"
29 #include "cross-staff.hh"
33 Group_interface g (this, "stems");
38 Beam::add_stem (Stem*s)
40 Group_interface gi (this, "stems");
43 s->add_dependency (this);
45 assert (!s->beam_l ());
46 s->set_elt_property ("beam", self_scm_);
48 if (!spanned_drul_[LEFT])
55 Beam::get_multiplicity () const
58 for (SCM s = get_elt_property ("stems"); gh_pair_p (s); s = gh_cdr (s))
60 Score_element * sc = unsmob_element (gh_car (s));
62 if (Stem * st = dynamic_cast<Stem*> (sc))
63 m = m >? st->beam_count (LEFT) >? st->beam_count (RIGHT);
69 After pre-processing all directions should be set.
70 Several post-processing routines (stem, slur, script) need stem/beam
72 Currenly, this means that beam has set all stem's directions.
73 [Alternatively, stems could set its own directions, according to
74 their beam, during 'final-pre-processing'.]
77 Beam::do_pre_processing ()
80 if (visible_stem_count () < 2)
82 warning (_ ("beam has less than two stems"));
83 set_elt_property ("transparent", SCM_BOOL_T);
86 if (!get_direction ())
87 set_direction (get_default_dir ());
90 set_stem_directions ();
99 Beam::get_default_dir () const
101 Drul_array<int> total;
102 total[UP] = total[DOWN] = 0;
103 Drul_array<int> count;
104 count[UP] = count[DOWN] = 0;
107 for (int i=0; i <stem_count (); i++)
108 do { // HUH -- waar slaat dit op?
110 int current = s->get_direction ()
111 ? (1 + d * s->get_direction ())/2
112 : s->get_center_distance ((Direction)-d);
120 } while (flip(&d) != DOWN);
123 [Ross] states that the majority of the notes dictates the
124 direction (and not the mean of "center distance")
126 But is that because it really looks better, or because he wants
127 to provide some real simple hands-on rules?
129 We have our doubts, so we simply provide all sensible alternatives.
131 If dir is not determined: up (see stem::get_default_dir ()) */
133 Direction beam_dir = CENTER;
134 Direction neutral_dir = (Direction)(int)paper_l ()->get_var ("stem_default_neutral_direction");
136 SCM a = get_elt_property ("beam-dir-algorithm");
138 if (a == ly_symbol2scm ("majority")) // should get default from paper.
139 beam_dir = (count[UP] == count[DOWN]) ? neutral_dir
140 : (count[UP] > count[DOWN]) ? UP : DOWN;
141 else if (a == ly_symbol2scm ("mean"))
142 // mean center distance
143 beam_dir = (total[UP] == total[DOWN]) ? neutral_dir
144 : (total[UP] > total[DOWN]) ? UP : DOWN;
145 else if (a == ly_symbol2scm ("median"))
147 // median center distance
148 if (count[DOWN] && count[UP])
150 beam_dir = (total[UP] / count[UP] == total[DOWN] / count[DOWN])
152 : (total[UP] / count[UP] > total[DOWN] / count[DOWN]) ? UP : DOWN;
156 beam_dir = (count[UP] == count[DOWN]) ? neutral_dir
157 : (count[UP] > count[DOWN]) ? UP : DOWN;
166 Set all stems with non-forced direction to beam direction.
167 Urg: non-forced should become `without/with unforced' direction,
168 once stem gets cleaned-up.
171 Beam::set_stem_directions ()
173 Direction d = get_direction ();
174 for (int i=0; i <stem_count (); i++)
177 SCM force = s->remove_elt_property ("dir-forced");
178 if (!gh_boolean_p (force) || !gh_scm2bool (force))
179 s->set_direction (d);
186 if (!auto_knee ("auto-interstaff-knee-gap", true))
187 auto_knee ("auto-knee-gap", false);
191 Simplistic auto-knees; only consider vertical gap between two
194 `Forced' stem directions are ignored. If you don't want auto-knees,
195 don't set, or unset autoKneeGap/autoInterstaffKneeGap.
198 Beam::auto_knee (String gap_str, bool interstaff_b)
202 SCM gap = get_elt_property (gap_str);
203 if (gh_number_p (gap))
205 int auto_gap_i = gh_scm2int (gap);
206 for (int i=1; i < stem_count (); i++)
208 bool is_b = (bool)(calc_interstaff_dist (stem (i), this)
209 - calc_interstaff_dist (stem (i-1), this));
210 int l_y = (int)(stem (i-1)->chord_start_f ())
211 + (int)calc_interstaff_dist (stem (i-1), this);
212 int r_y = (int)(stem (i)->chord_start_f ())
213 + (int)calc_interstaff_dist (stem (i), this);
214 int gap_i = r_y - l_y;
216 if ((abs (gap_i) >= auto_gap_i) && (!interstaff_b || is_b))
218 knee_y = (r_y + l_y) / 2;
226 for (int i=0; i < stem_count (); i++)
228 int y = (int)(stem (i)->chord_start_f ())
229 + (int)calc_interstaff_dist (stem (i), this);
230 stem (i)->set_direction (y < knee_y ? UP : DOWN);
231 stem (i)->set_elt_property ("dir-forced", SCM_BOOL_T);
238 Set stem's shorten property if unset.
239 TODO: take some y-position (nearest?) into account
242 Beam::set_stem_shorten ()
244 if (!visible_stem_count ())
247 Real forced_fraction = forced_stem_count () / visible_stem_count ();
248 if (forced_fraction < 0.5)
251 int multiplicity = get_multiplicity ();
252 SCM shorten = scm_eval (scm_listify (
253 ly_symbol2scm ("beamed-stem-shorten"),
254 gh_int2scm (multiplicity),
256 Real shorten_f = gh_scm2double (shorten)
257 * Staff_symbol_referencer_interface (this).staff_space ();
259 /* cute, but who invented this -- how to customise ? */
260 if (forced_fraction < 1)
263 for (int i=0; i < stem_count (); i++)
266 if (s->invisible_b ())
268 if (gh_number_p (s->get_elt_property ("shorten")))
269 s->set_elt_property ("shorten", gh_double2scm (shorten_f));
273 Set elt properties height and y-position if not set.
274 Adjust stem lengths to reach beam.
277 Beam::do_post_processing ()
279 /* first, calculate y, dy */
281 calc_position_and_height (&y, &dy);
282 if (suspect_slope_b (y, dy))
285 Real damped_dy = calc_slope_damping_f (dy);
286 Real quantised_dy = quantise_dy_f (damped_dy);
288 y += (dy - quantised_dy) / 2;
292 until here, we used only stem_info, which acts as if dir=up
294 y *= get_direction ();
295 dy *= get_direction ();
297 /* set or read dy as necessary */
298 SCM s = get_elt_property ("height");
300 dy = gh_scm2double (s);
302 set_elt_property ("height", gh_double2scm (dy));
304 /* set or read y as necessary */
305 s = get_elt_property ("y-position");
308 y = gh_scm2double (s);
309 set_stem_length (y, dy);
313 /* we can modify y, so we should quantise y */
314 Real y_shift = check_stem_length_f (y, dy);
316 y = quantise_y_f (y, dy, 0);
317 set_stem_length (y, dy);
318 y_shift = check_stem_length_f (y, dy);
320 Real internote_f = paper_l ()->get_var ("interline") / 2;
321 if (y_shift > internote_f / 4)
326 for significantly lengthened or shortened stems,
327 request quanting the other way.
330 if (abs (y_shift) > internote_f / 2)
331 quant_dir = sign (y_shift) * get_direction ();
332 y = quantise_y_f (y, dy, quant_dir);
333 set_stem_length (y, dy);
336 set_elt_property ("y-position", gh_double2scm (y));
341 See Documentation/tex/fonts.doc
344 Beam::calc_position_and_height (Real* y, Real* dy) const
347 if (visible_stem_count () <= 1)
350 Real first_ideal = first_visible_stem ()->calc_stem_info ().idealy_f_;
351 if (first_ideal == last_visible_stem ()->calc_stem_info ().idealy_f_)
359 Real x0 = first_visible_stem ()->hpos_f ();
360 for (int i=0; i < stem_count (); i++)
363 if (s->invisible_b ())
365 ls.input.push (Offset (s->hpos_f () - x0,
366 s->calc_stem_info ().idealy_f_));
369 ls.minimise (dydx, *y); // duh, takes references
371 Real dx = last_visible_stem ()->hpos_f () - x0;
376 Beam::suspect_slope_b (Real y, Real dy) const
379 steep slope running against lengthened stem is suspect
381 Real first_ideal = first_visible_stem ()->calc_stem_info ().idealy_f_;
382 Real last_ideal = last_visible_stem ()->calc_stem_info ().idealy_f_;
383 Real lengthened = paper_l ()->get_var ("beam_lengthened");
384 Real steep = paper_l ()->get_var ("beam_steep_slope");
386 Real dx = last_visible_stem ()->hpos_f () - first_visible_stem ()->hpos_f ();
389 if (((y - first_ideal > lengthened) && (dydx > steep))
390 || ((y + dy - last_ideal > lengthened) && (dydx < -steep)))
398 This neat trick is by Werner Lemberg,
399 damped = tanh (slope)
400 corresponds with some tables in [Wanske]
403 Beam::calc_slope_damping_f (Real dy) const
405 SCM damp = get_elt_property ("damping"); // remove?
406 int damping = 1; // ugh.
407 if (gh_number_p (damp))
408 damping = gh_scm2int (damp);
412 Real dx = last_visible_stem ()->hpos_f ()
413 - first_visible_stem ()->hpos_f ();
415 dydx = 0.6 * tanh (dydx) / damping;
422 Beam::calc_stem_y_f (Stem* s, Real y, Real dy) const
424 Real beam_f = gh_scm2double (get_elt_property ("beam-thickness"));
425 int multiplicity = get_multiplicity ();
428 Real interbeam_f = paper_l ()->interbeam_f (multiplicity);
429 Real x0 = first_visible_stem ()->hpos_f ();
430 Real dx = last_visible_stem ()->hpos_f () - x0;
431 Real stem_y = (s->hpos_f () - x0) / dx * dy + y;
434 if (get_direction () != s->get_direction ())
436 stem_y -= get_direction () * (beam_f / 2
437 + (multiplicity - 1) * interbeam_f);
439 Staff_symbol_referencer_interface me (s);
440 Staff_symbol_referencer_interface last (last_visible_stem ());
442 if ((s != first_visible_stem ())
443 && me.staff_symbol_l () != last.staff_symbol_l ())
444 stem_y += get_direction ()
445 * (multiplicity - (s->flag_i () - 2) >? 0) * interbeam_f;
451 Beam::check_stem_length_f (Real y, Real dy) const
455 for (int i=0; i < stem_count (); i++)
458 if (s->invisible_b ())
461 Real stem_y = calc_stem_y_f (s, y, dy);
463 stem_y *= get_direction ();
464 Stem_info info = s->calc_stem_info ();
466 if (stem_y > info.maxy_f_)
467 shorten = shorten <? info.maxy_f_ - stem_y;
469 if (stem_y < info.miny_f_)
470 lengthen = lengthen >? info.miny_f_ - stem_y;
473 if (lengthen && shorten)
474 warning (_ ("weird beam vertical offset"));
476 /* when all stems are too short, normal stems win */
478 return shorten * get_direction ();
480 return lengthen * get_direction ();
484 Beam::set_stem_length (Real y, Real dy)
486 Real internote_f = paper_l ()->get_var ("interline") / 2;
487 for (int i=0; i < stem_count (); i++)
490 if (s->invisible_b ())
493 Real stem_y = calc_stem_y_f (s, y, dy);
495 /* caution: stem measures in staff-positions */
496 s->set_stemend ((stem_y - calc_interstaff_dist (s, this)) / internote_f);
501 [Ross] (simplification of)
502 Try to set dy complying with:
504 - beam_f / 2 + staffline_f / 2
505 - beam_f + staffline_f
508 TODO: get allowed-positions as scm list (aarg: from paper block)
511 Beam::quantise_dy_f (Real dy) const
513 SCM s = get_elt_property ("slope-quantisation");
515 if (s == ly_symbol2scm ("none"))
518 Staff_symbol_referencer_interface st (this);
519 Real interline_f = st.staff_space ();
521 Real staffline_f = paper_l ()->get_var ("stafflinethickness");
522 Real beam_f = gh_scm2double (get_elt_property ("beam-thickness"));;
524 Array<Real> allowed_fraction (3);
525 allowed_fraction[0] = 0;
526 allowed_fraction[1] = (beam_f / 2 + staffline_f / 2);
527 allowed_fraction[2] = (beam_f + staffline_f);
529 allowed_fraction.push (interline_f);
530 Interval iv = quantise_iv (allowed_fraction, abs (dy));
531 Real q = (abs (dy) - iv[SMALLER] <= iv[BIGGER] - abs (dy))
535 return q * sign (dy);
539 Prevent interference from stafflines and beams.
540 See Documentation/tex/fonts.doc
542 TODO: get allowed-positions as scm list (aarg: from paper block)
545 Beam::quantise_y_f (Real y, Real dy, int quant_dir)
548 We only need to quantise the (left) y-position of the beam,
549 since dy is quantised too.
550 if extend_b then stems must *not* get shorter
552 SCM s = get_elt_property ("slope-quantisation");
553 if (s == ly_symbol2scm ("none"))
557 ----------------------------------------------------------
561 --------------########------------------------------------
564 hang straddle sit inter hang
567 Staff_symbol_referencer_interface sinf (this);
568 Real space = sinf.staff_space ();
569 Real staffline_f = paper_l ()->get_var ("stafflinethickness");
570 Real beam_f = gh_scm2double (get_elt_property ("beam-thickness"));;
573 Real sit = beam_f / 2 - staffline_f / 2;
574 Real hang = space - beam_f / 2 + staffline_f / 2;
577 Put all allowed positions into an array.
578 Whether a position is allowed or not depends on
579 strictness of quantisation, multiplicity and direction.
581 For simplicity, we'll assume dir = UP and correct if
582 dir = DOWN afterwards.
585 int multiplicity = get_multiplicity ();
588 Array<Real> allowed_position;
589 if (s == ly_symbol2scm ("normal"))
591 if ((multiplicity <= 2) || (abs (dy) >= staffline_f / 2))
592 allowed_position.push (straddle);
593 if ((multiplicity <= 1) || (abs (dy) >= staffline_f / 2))
594 allowed_position.push (sit);
595 allowed_position.push (hang);
597 else if (s == ly_symbol2scm ("traditional"))
599 // TODO: check and fix TRADITIONAL
600 if ((multiplicity <= 2) || (abs (dy) >= staffline_f / 2))
601 allowed_position.push (straddle);
602 if ((multiplicity <= 1) && (dy <= staffline_f / 2))
603 allowed_position.push (sit);
604 if (dy >= -staffline_f / 2)
605 allowed_position.push (hang);
608 allowed_position.push (space);
609 Real up_y = get_direction () * y;
610 Interval iv = quantise_iv (allowed_position, up_y);
612 Real q = up_y - iv[SMALLER] <= iv[BIGGER] - up_y
613 ? iv[SMALLER] : iv[BIGGER];
615 q = iv[(Direction)quant_dir];
617 return q * get_direction ();
621 Beam::set_beaming (Beaming_info_list *beaming)
624 for (int i=0; i < stem_count (); i++)
628 if (stem (i)->beam_count (d) == 0)
629 stem (i)->set_beaming ( beaming->infos_.elem (i).beams_i_drul_[d],d);
631 while (flip (&d) != LEFT);
638 beams to go with one stem.
644 Beam::stem_beams (Stem *here, Stem *next, Stem *prev) const
646 if ((next && !(next->hpos_f () > here->hpos_f ())) ||
647 (prev && !(prev->hpos_f () < here->hpos_f ())))
648 programming_error ("Beams are not left-to-right");
650 Real staffline_f = paper_l ()->get_var ("stafflinethickness");
651 int multiplicity = get_multiplicity ();
654 Real interbeam_f = paper_l ()->interbeam_f (multiplicity);
655 Real beam_f = gh_scm2double (get_elt_property ("beam-thickness"));;
657 Real dy = interbeam_f;
658 Real stemdx = staffline_f;
660 Real dx = last_visible_stem ()->hpos_f () - first_visible_stem ()->hpos_f ();
661 Real dydx = get_real ("height")/dx;
668 if (!here->first_head ())
670 else if (here->type_i ()== 1)
671 nw_f = paper_l ()->get_var ("wholewidth");
672 else if (here->type_i () == 2)
673 nw_f = paper_l ()->get_var ("notewidth") * 0.8;
675 nw_f = paper_l ()->get_var ("quartwidth");
677 /* half beams extending to the left. */
680 int lhalfs= lhalfs = here->beam_count (LEFT) - prev->beam_count (RIGHT);
681 int lwholebeams= here->beam_count (LEFT) <? prev->beam_count (RIGHT) ;
683 Half beam should be one note-width,
684 but let's make sure two half-beams never touch
686 Real w = here->hpos_f () - prev->hpos_f ();
689 if (lhalfs) // generates warnings if not
690 a = lookup_l ()->beam (dydx, w, beam_f);
691 a.translate (Offset (-w, -w * dydx));
692 for (int j = 0; j < lhalfs; j++)
695 b.translate_axis (-get_direction () * dy * (lwholebeams+j), Y_AXIS);
696 leftbeams.add_molecule (b);
702 int rhalfs = here->beam_count (RIGHT) - next->beam_count (LEFT);
703 int rwholebeams= here->beam_count (RIGHT) <? next->beam_count (LEFT) ;
705 Real w = next->hpos_f () - here->hpos_f ();
706 Molecule a = lookup_l ()->beam (dydx, w + stemdx, beam_f);
707 a.translate_axis( - stemdx/2, X_AXIS);
711 SCM gap = get_elt_property ("beam-gap");
712 if (gh_number_p (gap))
714 int gap_i = gh_scm2int ( (gap));
715 int nogap = rwholebeams - gap_i;
717 for (; j < nogap; j++)
720 b.translate_axis (-get_direction () * dy * j, Y_AXIS);
721 rightbeams.add_molecule (b);
723 // TODO: notehead widths differ for different types
726 a = lookup_l ()->beam (dydx, w + stemdx, beam_f);
729 for (; j < rwholebeams; j++)
732 if (!here->invisible_b ())
733 b.translate (Offset (gap_f, -get_direction () * dy * j));
735 b.translate (Offset (0, -get_direction () * dy * j));
736 rightbeams.add_molecule (b);
741 a = lookup_l ()->beam (dydx, w, beam_f);
743 for (; j < rwholebeams + rhalfs; j++)
746 b.translate_axis (-get_direction () * dy * j, Y_AXIS);
747 rightbeams.add_molecule (b);
751 leftbeams.add_molecule (rightbeams);
754 Does beam quanting think of the asymetry of beams?
755 Refpoint is on bottom of symbol. (FIXTHAT) --hwn.
762 Beam::do_brew_molecule_p () const
764 Molecule *mol_p = new Molecule;
768 Real x0 = first_visible_stem ()->hpos_f ();
769 Real dx = last_visible_stem ()->hpos_f () - x0;
770 Real dydx = get_real ("height")/dx;
771 Real y = get_real ("y-position");
772 for (int j=0; j <stem_count (); j++)
775 Stem * prev = (j > 0)? stem (j-1) : 0;
776 Stem * next = (j < stem_count ()-1) ? stem (j+1) :0;
778 Molecule sb = stem_beams (i, next, prev);
779 Real x = i->hpos_f ()-x0;
780 sb.translate (Offset (x, x * dydx + y));
781 mol_p->add_molecule (sb);
783 mol_p->translate_axis (x0
784 - spanned_drul_[LEFT]->relative_coordinate (0, X_AXIS), X_AXIS);
790 Beam::forced_stem_count () const
793 for (int i=0; i < stem_count (); i++)
797 if (s->invisible_b ())
800 if (((int)s->chord_start_f ())
801 && (s->get_direction () != s->get_default_dir ()))
810 TODO: Fix this class. This is wildly inefficient.
811 And it sux. Yet another array/list 'interface'.
814 Beam::stem (int i) const
816 return Group_interface__extract_elements ((Beam*) this, (Stem*) 0, "stems")[i];
820 Beam::stem_count () const
822 Group_interface gi (this, "stems");
827 Beam::stem_top () const
829 SCM s = get_elt_property ("stems");
831 return gh_pair_p (s) ? dynamic_cast<Stem*> (unsmob_element (gh_car (s))) : 0;
833 //Group_interface__extract_elements ((Beam*) this, (Stem*) 0, "stems")[stem_count () - 1];
838 Beam::visible_stem_count () const
841 for (int i = 0; i < stem_count (); i++)
843 if (!stem (i)->invisible_b ())
850 Beam::first_visible_stem () const
852 for (int i = 0; i < stem_count (); i++)
855 if (!s->invisible_b ())
865 Beam::last_visible_stem () const
867 for (int i = stem_count (); i > 0; i--)
869 Stem* s = stem (i - 1);
870 if (!s->invisible_b ())