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"
30 #include "lily-guile.icc"
34 Group_interface g (this, "stems");
39 Beam::add_stem (Stem*s)
41 Group_interface gi (this, "stems");
44 s->add_dependency (this);
46 assert (!s->beam_l ());
47 s->set_elt_property ("beam", self_scm_);
49 if (!spanned_drul_[LEFT])
56 Beam::get_multiplicity () const
59 for (SCM s = get_elt_property ("stems"); gh_pair_p (s); s = gh_cdr (s))
61 Score_element * sc = unsmob_element (gh_car (s));
63 if (Stem * st = dynamic_cast<Stem*> (sc))
64 m = m >? st->beam_count (LEFT) >? st->beam_count (RIGHT);
70 After pre-processing all directions should be set.
71 Several post-processing routines (stem, slur, script) need stem/beam
73 Currenly, this means that beam has set all stem's directions.
74 [Alternatively, stems could set its own directions, according to
75 their beam, during 'final-pre-processing'.]
78 Beam::do_pre_processing ()
81 if (visible_stem_count () < 2)
83 warning (_ ("beam has less than two stems"));
84 set_elt_property ("transparent", SCM_BOOL_T);
87 if (!get_direction ())
88 set_direction (get_default_dir ());
91 set_stem_directions ();
100 Beam::get_default_dir () const
102 Drul_array<int> total;
103 total[UP] = total[DOWN] = 0;
104 Drul_array<int> count;
105 count[UP] = count[DOWN] = 0;
108 for (int i=0; i <stem_count (); i++)
109 do { // HUH -- waar slaat dit op?
111 int current = s->get_direction ()
112 ? (1 + d * s->get_direction ())/2
113 : s->get_center_distance ((Direction)-d);
121 } while (flip(&d) != DOWN);
124 [Ross] states that the majority of the notes dictates the
125 direction (and not the mean of "center distance")
127 But is that because it really looks better, or because he wants
128 to provide some real simple hands-on rules?
130 We have our doubts, so we simply provide all sensible alternatives.
132 If dir is not determined: up (see stem::get_default_dir ()) */
134 Direction beam_dir = CENTER;
135 Direction neutral_dir = (Direction)(int)paper_l ()->get_var ("stem_default_neutral_direction");
137 SCM a = get_elt_property ("beam-dir-algorithm");
139 if (a == ly_symbol2scm ("majority")) // should get default from paper.
140 beam_dir = (count[UP] == count[DOWN]) ? neutral_dir
141 : (count[UP] > count[DOWN]) ? UP : DOWN;
142 else if (a == ly_symbol2scm ("mean"))
143 // mean center distance
144 beam_dir = (total[UP] == total[DOWN]) ? neutral_dir
145 : (total[UP] > total[DOWN]) ? UP : DOWN;
146 else if (a == ly_symbol2scm ("median"))
148 // median center distance
149 if (count[DOWN] && count[UP])
151 beam_dir = (total[UP] / count[UP] == total[DOWN] / count[DOWN])
153 : (total[UP] / count[UP] > total[DOWN] / count[DOWN]) ? UP : DOWN;
157 beam_dir = (count[UP] == count[DOWN]) ? neutral_dir
158 : (count[UP] > count[DOWN]) ? UP : DOWN;
167 Set all stems with non-forced direction to beam direction.
168 Urg: non-forced should become `without/with unforced' direction,
169 once stem gets cleaned-up.
172 Beam::set_stem_directions ()
174 Direction d = get_direction ();
175 for (int i=0; i <stem_count (); i++)
178 SCM force = s->remove_elt_property ("dir-forced");
179 if (!gh_boolean_p (force) || !gh_scm2bool (force))
180 s->set_direction (d);
187 if (!auto_knee ("auto-interstaff-knee-gap", true))
188 auto_knee ("auto-knee-gap", false);
192 Simplistic auto-knees; only consider vertical gap between two
195 `Forced' stem directions are ignored. If you don't want auto-knees,
196 don't set, or unset autoKneeGap/autoInterstaffKneeGap.
199 Beam::auto_knee (String gap_str, bool interstaff_b)
203 SCM gap = get_elt_property (gap_str);
204 if (gh_number_p (gap))
206 int auto_gap_i = gh_scm2int (gap);
207 for (int i=1; i < stem_count (); i++)
209 bool is_b = (bool)(calc_interstaff_dist (stem (i), this)
210 - calc_interstaff_dist (stem (i-1), this));
211 int l_y = (int)(stem (i-1)->head_positions()[get_direction ()])
212 + (int)calc_interstaff_dist (stem (i-1), this);
213 int r_y = (int)(stem (i)->head_positions()[get_direction ()])
214 + (int)calc_interstaff_dist (stem (i), this);
215 int gap_i = r_y - l_y;
217 if ((abs (gap_i) >= auto_gap_i) && (!interstaff_b || is_b))
219 knee_y = (r_y + l_y) / 2;
227 for (int i=0; i < stem_count (); i++)
229 int y = (int)(stem (i)->head_positions()[get_direction ()])
230 + (int)calc_interstaff_dist (stem (i), this);
231 stem (i)->set_direction (y < knee_y ? UP : DOWN);
232 stem (i)->set_elt_property ("dir-forced", SCM_BOOL_T);
239 Set stem's shorten property if unset.
241 take some y-position (chord/beam/nearest?) into account
242 scmify forced-fraction
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 ();
256 SCM shorten = ly_eval_str ("beamed-stem-shorten");
259 scm_to_array (shorten, &a);
263 Staff_symbol_referencer_interface st (this);
264 Real staff_space = st.staff_space ();
265 Real shorten_f = a[multiplicity <? (a.size () - 1)] * staff_space;
267 /* cute, but who invented this -- how to customise ? */
268 if (forced_fraction < 1)
271 for (int i=0; i < stem_count (); i++)
274 if (s->invisible_b ())
276 if (gh_number_p (s->get_elt_property ("shorten")))
277 s->set_elt_property ("shorten", gh_double2scm (shorten_f));
282 Set elt properties height and y-position if not set.
283 Adjust stem lengths to reach beam.
286 Beam::do_post_processing ()
288 /* first, calculate y, dy */
290 calc_position_and_height (&y, &dy);
291 if (suspect_slope_b (y, dy))
294 Real damped_dy = calc_slope_damping_f (dy);
295 Real quantised_dy = quantise_dy_f (damped_dy);
297 y += (dy - quantised_dy) / 2;
301 until here, we used only stem_info, which acts as if dir=up
303 y *= get_direction ();
304 dy *= get_direction ();
306 /* set or read dy as necessary */
307 SCM s = get_elt_property ("height");
309 dy = gh_scm2double (s);
311 set_elt_property ("height", gh_double2scm (dy));
313 /* set or read y as necessary */
314 s = get_elt_property ("y-position");
317 y = gh_scm2double (s);
318 set_stem_length (y, dy);
322 /* we can modify y, so we should quantise y */
323 Real y_shift = check_stem_length_f (y, dy);
325 y = quantise_y_f (y, dy, 0);
326 set_stem_length (y, dy);
327 y_shift = check_stem_length_f (y, dy);
329 Staff_symbol_referencer_interface st (this);
330 Real half_space = st.staff_space () / 2;
331 if (y_shift > half_space / 4)
336 for significantly lengthened or shortened stems,
337 request quanting the other way.
340 if (abs (y_shift) > half_space / 2)
341 quant_dir = sign (y_shift) * get_direction ();
342 y = quantise_y_f (y, dy, quant_dir);
343 set_stem_length (y, dy);
346 set_elt_property ("y-position", gh_double2scm (y));
351 See Documentation/tex/fonts.doc
354 Beam::calc_position_and_height (Real* y, Real* dy) const
357 if (visible_stem_count () <= 1)
360 Real first_ideal = first_visible_stem ()->calc_stem_info ().idealy_f_;
361 if (first_ideal == last_visible_stem ()->calc_stem_info ().idealy_f_)
369 Real x0 = first_visible_stem ()->hpos_f ();
370 for (int i=0; i < stem_count (); i++)
373 if (s->invisible_b ())
375 ls.input.push (Offset (s->hpos_f () - x0,
376 s->calc_stem_info ().idealy_f_));
379 ls.minimise (dydx, *y); // duh, takes references
381 Real dx = last_visible_stem ()->hpos_f () - x0;
386 Beam::suspect_slope_b (Real y, Real dy) const
389 steep slope running against lengthened stem is suspect
391 Real first_ideal = first_visible_stem ()->calc_stem_info ().idealy_f_;
392 Real last_ideal = last_visible_stem ()->calc_stem_info ().idealy_f_;
393 Real lengthened = paper_l ()->get_var ("beam_lengthened");
394 Real steep = paper_l ()->get_var ("beam_steep_slope");
396 Real dx = last_visible_stem ()->hpos_f () - first_visible_stem ()->hpos_f ();
399 if (((y - first_ideal > lengthened) && (dydx > steep))
400 || ((y + dy - last_ideal > lengthened) && (dydx < -steep)))
408 This neat trick is by Werner Lemberg,
409 damped = tanh (slope)
410 corresponds with some tables in [Wanske]
413 Beam::calc_slope_damping_f (Real dy) const
415 SCM damp = get_elt_property ("damping"); // remove?
416 int damping = 1; // ugh.
417 if (gh_number_p (damp))
418 damping = gh_scm2int (damp);
422 Real dx = last_visible_stem ()->hpos_f ()
423 - first_visible_stem ()->hpos_f ();
425 dydx = 0.6 * tanh (dydx) / damping;
432 Beam::calc_stem_y_f (Stem* s, Real y, Real dy) const
434 Real thick = gh_scm2double (get_elt_property ("beam-thickness"));
435 int beam_multiplicity = get_multiplicity ();
436 int stem_multiplicity = (s->flag_i () - 2) >? 0;
438 Real interbeam_f = paper_l ()->interbeam_f (beam_multiplicity);
439 Real x0 = first_visible_stem ()->hpos_f ();
440 Real dx = last_visible_stem ()->hpos_f () - x0;
441 Real stem_y = (s->hpos_f () - x0) / dx * dy + y;
444 if (get_direction () != s->get_direction ())
446 stem_y -= get_direction ()
447 * (thick / 2 + (beam_multiplicity - 1 - stem_multiplicity))
450 Staff_symbol_referencer_interface me (s);
451 Staff_symbol_referencer_interface last (last_visible_stem ());
453 if ((s != first_visible_stem ())
454 && me.staff_symbol_l () != last.staff_symbol_l ())
455 stem_y += get_direction ()
456 * (beam_multiplicity - stem_multiplicity) * interbeam_f;
462 Beam::check_stem_length_f (Real y, Real dy) const
466 for (int i=0; i < stem_count (); i++)
469 if (s->invisible_b ())
472 Real stem_y = calc_stem_y_f (s, y, dy);
474 stem_y *= get_direction ();
475 Stem_info info = s->calc_stem_info ();
477 if (stem_y > info.maxy_f_)
478 shorten = shorten <? info.maxy_f_ - stem_y;
480 if (stem_y < info.miny_f_)
481 lengthen = lengthen >? info.miny_f_ - stem_y;
484 if (lengthen && shorten)
485 warning (_ ("weird beam vertical offset"));
487 /* when all stems are too short, normal stems win */
489 return shorten * get_direction ();
491 return lengthen * get_direction ();
495 Hmm. At this time, beam position and slope are determined. Maybe,
496 stem directions and length should set to relative to the chord's
497 position of the beam. */
499 Beam::set_stem_length (Real y, Real dy)
501 Staff_symbol_referencer_interface st (this);
502 Real half_space = st.staff_space ()/2;
503 for (int i=0; i < stem_count (); i++)
506 if (s->invisible_b ())
509 Real stem_y = calc_stem_y_f (s, y, dy);
511 /* caution: stem measures in staff-positions */
512 s->set_stemend ((stem_y + calc_interstaff_dist (s, this)) / half_space);
517 [Ross] (simplification of)
518 Set dy complying with:
520 - thick / 2 + staffline_f / 2
521 - thick + staffline_f
525 Beam::quantise_dy_f (Real dy) const
527 SCM quants = ly_eval_str ("beam-height-quants");
530 scm_to_array (quants, &a);
534 Staff_symbol_referencer_interface st (this);
535 Real staff_space = st.staff_space ();
537 Interval iv = quantise_iv (a, abs (dy)/staff_space) * staff_space;
538 Real q = (abs (dy) - iv[SMALLER] <= iv[BIGGER] - abs (dy))
542 return q * sign (dy);
546 Prevent interference from stafflines and beams.
547 See Documentation/tex/fonts.doc
549 We only need to quantise the (left) y-position of the beam,
550 since dy is quantised too.
551 if extend_b then stems must *not* get shorter
554 Beam::quantise_y_f (Real y, Real dy, int quant_dir)
556 int multiplicity = get_multiplicity ();
557 Staff_symbol_referencer_interface st (this);
558 Real staff_space = st.staff_space ();
559 SCM quants = scm_eval (gh_list (
560 ly_symbol2scm ("beam-vertical-position-quants"),
561 gh_int2scm (multiplicity),
562 gh_double2scm (dy/staff_space),
565 scm_to_array (quants, &a);
569 Real up_y = get_direction () * y;
570 Interval iv = quantise_iv (a, up_y/staff_space) * staff_space;
572 Real q = up_y - iv[SMALLER] <= iv[BIGGER] - up_y
573 ? iv[SMALLER] : iv[BIGGER];
575 q = iv[(Direction)quant_dir];
577 return q * get_direction ();
581 Beam::set_beaming (Beaming_info_list *beaming)
584 for (int i=0; i < stem_count (); i++)
588 if (stem (i)->beam_count (d) == 0)
589 stem (i)->set_beaming ( beaming->infos_.elem (i).beams_i_drul_[d],d);
591 while (flip (&d) != LEFT);
598 beams to go with one stem.
604 Beam::stem_beams (Stem *here, Stem *next, Stem *prev) const
606 if ((next && !(next->hpos_f () > here->hpos_f ())) ||
607 (prev && !(prev->hpos_f () < here->hpos_f ())))
608 programming_error ("Beams are not left-to-right");
610 Real staffline_f = paper_l ()->get_var ("stafflinethickness");
611 int multiplicity = get_multiplicity ();
614 Real interbeam_f = paper_l ()->interbeam_f (multiplicity);
615 Real thick = gh_scm2double (get_elt_property ("beam-thickness"));;
617 Real dy = interbeam_f;
618 Real stemdx = staffline_f;
620 Real dx = last_visible_stem ()->hpos_f () - first_visible_stem ()->hpos_f ();
621 Real dydx = get_real ("height")/dx;
628 if (!here->first_head ())
630 else if (here->type_i ()== 1)
631 nw_f = paper_l ()->get_var ("wholewidth");
632 else if (here->type_i () == 2)
633 nw_f = paper_l ()->get_var ("notewidth") * 0.8;
635 nw_f = paper_l ()->get_var ("quartwidth");
637 /* half beams extending to the left. */
640 int lhalfs= lhalfs = here->beam_count (LEFT) - prev->beam_count (RIGHT);
641 int lwholebeams= here->beam_count (LEFT) <? prev->beam_count (RIGHT) ;
643 Half beam should be one note-width,
644 but let's make sure two half-beams never touch
646 Real w = here->hpos_f () - prev->hpos_f ();
649 if (lhalfs) // generates warnings if not
650 a = lookup_l ()->beam (dydx, w, thick);
651 a.translate (Offset (-w, -w * dydx));
652 for (int j = 0; j < lhalfs; j++)
655 b.translate_axis (-get_direction () * dy * (lwholebeams+j), Y_AXIS);
656 leftbeams.add_molecule (b);
662 int rhalfs = here->beam_count (RIGHT) - next->beam_count (LEFT);
663 int rwholebeams= here->beam_count (RIGHT) <? next->beam_count (LEFT) ;
665 Real w = next->hpos_f () - here->hpos_f ();
666 Molecule a = lookup_l ()->beam (dydx, w + stemdx, thick);
667 a.translate_axis( - stemdx/2, X_AXIS);
671 SCM gap = get_elt_property ("beam-gap");
672 if (gh_number_p (gap))
674 int gap_i = gh_scm2int ( (gap));
675 int nogap = rwholebeams - gap_i;
677 for (; j < nogap; j++)
680 b.translate_axis (-get_direction () * dy * j, Y_AXIS);
681 rightbeams.add_molecule (b);
683 // TODO: notehead widths differ for different types
686 a = lookup_l ()->beam (dydx, w + stemdx, thick);
689 for (; j < rwholebeams; j++)
692 if (!here->invisible_b ())
693 b.translate (Offset (gap_f, -get_direction () * dy * j));
695 b.translate (Offset (0, -get_direction () * dy * j));
696 rightbeams.add_molecule (b);
701 a = lookup_l ()->beam (dydx, w, thick);
703 for (; j < rwholebeams + rhalfs; j++)
706 b.translate_axis (-get_direction () * dy * j, Y_AXIS);
707 rightbeams.add_molecule (b);
711 leftbeams.add_molecule (rightbeams);
714 Does beam quanting think of the asymetry of beams?
715 Refpoint is on bottom of symbol. (FIXTHAT) --hwn.
722 Beam::do_brew_molecule_p () const
724 Molecule *mol_p = new Molecule;
728 Real x0 = first_visible_stem ()->hpos_f ();
729 Real dx = last_visible_stem ()->hpos_f () - x0;
730 Real dydx = get_real ("height")/dx;
731 Real y = get_real ("y-position");
732 for (int j=0; j <stem_count (); j++)
735 Stem * prev = (j > 0)? stem (j-1) : 0;
736 Stem * next = (j < stem_count ()-1) ? stem (j+1) :0;
738 Molecule sb = stem_beams (i, next, prev);
739 Real x = i->hpos_f ()-x0;
740 sb.translate (Offset (x, x * dydx + y));
741 mol_p->add_molecule (sb);
743 mol_p->translate_axis (x0
744 - spanned_drul_[LEFT]->relative_coordinate (0, X_AXIS), X_AXIS);
750 Beam::forced_stem_count () const
753 for (int i=0; i < stem_count (); i++)
757 if (s->invisible_b ())
760 if (((int)s->chord_start_f ())
761 && (s->get_direction () != s->get_default_dir ()))
770 TODO: Fix this class. This is wildly inefficient.
771 And it sux. Yet another array/list 'interface'.
774 Beam::stem (int i) const
776 return Group_interface__extract_elements ((Beam*) this, (Stem*) 0, "stems")[i];
780 Beam::stem_count () const
782 Group_interface gi (this, "stems");
787 Beam::stem_top () const
789 SCM s = get_elt_property ("stems");
791 return gh_pair_p (s) ? dynamic_cast<Stem*> (unsmob_element (gh_car (s))) : 0;
793 //Group_interface__extract_elements ((Beam*) this, (Stem*) 0, "stems")[stem_count () - 1];
798 Beam::visible_stem_count () const
801 for (int i = 0; i < stem_count (); i++)
803 if (!stem (i)->invisible_b ())
810 Beam::first_visible_stem () const
812 for (int i = 0; i < stem_count (); i++)
815 if (!s->invisible_b ())
825 Beam::last_visible_stem () const
827 for (int i = stem_count (); i > 0; i--)
829 Stem* s = stem (i - 1);
830 if (!s->invisible_b ())