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.
240 TODO: take some y-position (nearest?) into account
243 Beam::set_stem_shorten ()
245 if (!visible_stem_count ())
248 Real forced_fraction = forced_stem_count () / visible_stem_count ();
249 if (forced_fraction < 0.5)
252 int multiplicity = get_multiplicity ();
253 SCM shorten = scm_eval (gh_list (
254 ly_symbol2scm ("beamed-stem-shorten"),
255 gh_int2scm (multiplicity),
257 Real shorten_f = gh_scm2double (shorten)
258 * Staff_symbol_referencer_interface (this).staff_space ();
260 /* cute, but who invented this -- how to customise ? */
261 if (forced_fraction < 1)
264 for (int i=0; i < stem_count (); i++)
267 if (s->invisible_b ())
269 if (gh_number_p (s->get_elt_property ("shorten")))
270 s->set_elt_property ("shorten", gh_double2scm (shorten_f));
274 Set elt properties height and y-position if not set.
275 Adjust stem lengths to reach beam.
278 Beam::do_post_processing ()
280 /* first, calculate y, dy */
282 calc_position_and_height (&y, &dy);
283 if (suspect_slope_b (y, dy))
286 Real damped_dy = calc_slope_damping_f (dy);
287 Real quantised_dy = quantise_dy_f (damped_dy);
289 y += (dy - quantised_dy) / 2;
293 until here, we used only stem_info, which acts as if dir=up
295 y *= get_direction ();
296 dy *= get_direction ();
298 /* set or read dy as necessary */
299 SCM s = get_elt_property ("height");
301 dy = gh_scm2double (s);
303 set_elt_property ("height", gh_double2scm (dy));
305 /* set or read y as necessary */
306 s = get_elt_property ("y-position");
309 y = gh_scm2double (s);
310 set_stem_length (y, dy);
314 /* we can modify y, so we should quantise y */
315 Real y_shift = check_stem_length_f (y, dy);
317 y = quantise_y_f (y, dy, 0);
318 set_stem_length (y, dy);
319 y_shift = check_stem_length_f (y, dy);
321 Staff_symbol_referencer_interface st (this);
322 Real half_space = st.staff_space () / 2;
323 if (y_shift > half_space / 4)
328 for significantly lengthened or shortened stems,
329 request quanting the other way.
332 if (abs (y_shift) > half_space / 2)
333 quant_dir = sign (y_shift) * get_direction ();
334 y = quantise_y_f (y, dy, quant_dir);
335 set_stem_length (y, dy);
338 set_elt_property ("y-position", gh_double2scm (y));
343 See Documentation/tex/fonts.doc
346 Beam::calc_position_and_height (Real* y, Real* dy) const
349 if (visible_stem_count () <= 1)
352 Real first_ideal = first_visible_stem ()->calc_stem_info ().idealy_f_;
353 if (first_ideal == last_visible_stem ()->calc_stem_info ().idealy_f_)
361 Real x0 = first_visible_stem ()->hpos_f ();
362 for (int i=0; i < stem_count (); i++)
365 if (s->invisible_b ())
367 ls.input.push (Offset (s->hpos_f () - x0,
368 s->calc_stem_info ().idealy_f_));
371 ls.minimise (dydx, *y); // duh, takes references
373 Real dx = last_visible_stem ()->hpos_f () - x0;
378 Beam::suspect_slope_b (Real y, Real dy) const
381 steep slope running against lengthened stem is suspect
383 Real first_ideal = first_visible_stem ()->calc_stem_info ().idealy_f_;
384 Real last_ideal = last_visible_stem ()->calc_stem_info ().idealy_f_;
385 Real lengthened = paper_l ()->get_var ("beam_lengthened");
386 Real steep = paper_l ()->get_var ("beam_steep_slope");
388 Real dx = last_visible_stem ()->hpos_f () - first_visible_stem ()->hpos_f ();
391 if (((y - first_ideal > lengthened) && (dydx > steep))
392 || ((y + dy - last_ideal > lengthened) && (dydx < -steep)))
400 This neat trick is by Werner Lemberg,
401 damped = tanh (slope)
402 corresponds with some tables in [Wanske]
405 Beam::calc_slope_damping_f (Real dy) const
407 SCM damp = get_elt_property ("damping"); // remove?
408 int damping = 1; // ugh.
409 if (gh_number_p (damp))
410 damping = gh_scm2int (damp);
414 Real dx = last_visible_stem ()->hpos_f ()
415 - first_visible_stem ()->hpos_f ();
417 dydx = 0.6 * tanh (dydx) / damping;
424 Beam::calc_stem_y_f (Stem* s, Real y, Real dy) const
426 Real thick = gh_scm2double (get_elt_property ("beam-thickness"));
427 int beam_multiplicity = get_multiplicity ();
428 int stem_multiplicity = (s->flag_i () - 2) >? 0;
430 Real interbeam_f = paper_l ()->interbeam_f (beam_multiplicity);
431 Real x0 = first_visible_stem ()->hpos_f ();
432 Real dx = last_visible_stem ()->hpos_f () - x0;
433 Real stem_y = (s->hpos_f () - x0) / dx * dy + y;
436 if (get_direction () != s->get_direction ())
438 stem_y -= get_direction ()
439 * (thick / 2 + (beam_multiplicity - 1 - stem_multiplicity))
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 * (beam_multiplicity - stem_multiplicity) * 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 Hmm. At this time, beam position and slope are determined. Maybe,
488 stem directions and length should set to relative to the chord's
489 position of the beam. */
491 Beam::set_stem_length (Real y, Real dy)
493 Staff_symbol_referencer_interface st (this);
494 Real half_space = st.staff_space ()/2;
495 for (int i=0; i < stem_count (); i++)
498 if (s->invisible_b ())
501 Real stem_y = calc_stem_y_f (s, y, dy);
503 /* caution: stem measures in staff-positions */
504 s->set_stemend ((stem_y + calc_interstaff_dist (s, this)) / half_space);
509 [Ross] (simplification of)
510 Set dy complying with:
512 - thick / 2 + staffline_f / 2
513 - thick + staffline_f
517 Beam::quantise_dy_f (Real dy) const
519 SCM quants = ly_eval_str ("beam-height-quants");
522 scm_to_array (quants, &a);
526 Staff_symbol_referencer_interface st (this);
527 Real staff_space = st.staff_space ();
529 Interval iv = quantise_iv (a, abs (dy)/staff_space) * staff_space;
530 Real q = (abs (dy) - iv[SMALLER] <= iv[BIGGER] - abs (dy))
534 return q * sign (dy);
538 Prevent interference from stafflines and beams.
539 See Documentation/tex/fonts.doc
541 We only need to quantise the (left) y-position of the beam,
542 since dy is quantised too.
543 if extend_b then stems must *not* get shorter
546 Beam::quantise_y_f (Real y, Real dy, int quant_dir)
548 int multiplicity = get_multiplicity ();
549 Staff_symbol_referencer_interface st (this);
550 Real staff_space = st.staff_space ();
551 SCM quants = scm_eval (gh_list (
552 ly_symbol2scm ("beam-vertical-position-quants"),
553 gh_int2scm (multiplicity),
554 gh_double2scm (dy/staff_space),
557 scm_to_array (quants, &a);
561 Real up_y = get_direction () * y;
562 Interval iv = quantise_iv (a, up_y/staff_space) * staff_space;
564 Real q = up_y - iv[SMALLER] <= iv[BIGGER] - up_y
565 ? iv[SMALLER] : iv[BIGGER];
567 q = iv[(Direction)quant_dir];
569 return q * get_direction ();
573 Beam::set_beaming (Beaming_info_list *beaming)
576 for (int i=0; i < stem_count (); i++)
580 if (stem (i)->beam_count (d) == 0)
581 stem (i)->set_beaming ( beaming->infos_.elem (i).beams_i_drul_[d],d);
583 while (flip (&d) != LEFT);
590 beams to go with one stem.
596 Beam::stem_beams (Stem *here, Stem *next, Stem *prev) const
598 if ((next && !(next->hpos_f () > here->hpos_f ())) ||
599 (prev && !(prev->hpos_f () < here->hpos_f ())))
600 programming_error ("Beams are not left-to-right");
602 Real staffline_f = paper_l ()->get_var ("stafflinethickness");
603 int multiplicity = get_multiplicity ();
606 Real interbeam_f = paper_l ()->interbeam_f (multiplicity);
607 Real thick = gh_scm2double (get_elt_property ("beam-thickness"));;
609 Real dy = interbeam_f;
610 Real stemdx = staffline_f;
612 Real dx = last_visible_stem ()->hpos_f () - first_visible_stem ()->hpos_f ();
613 Real dydx = get_real ("height")/dx;
620 if (!here->first_head ())
622 else if (here->type_i ()== 1)
623 nw_f = paper_l ()->get_var ("wholewidth");
624 else if (here->type_i () == 2)
625 nw_f = paper_l ()->get_var ("notewidth") * 0.8;
627 nw_f = paper_l ()->get_var ("quartwidth");
629 /* half beams extending to the left. */
632 int lhalfs= lhalfs = here->beam_count (LEFT) - prev->beam_count (RIGHT);
633 int lwholebeams= here->beam_count (LEFT) <? prev->beam_count (RIGHT) ;
635 Half beam should be one note-width,
636 but let's make sure two half-beams never touch
638 Real w = here->hpos_f () - prev->hpos_f ();
641 if (lhalfs) // generates warnings if not
642 a = lookup_l ()->beam (dydx, w, thick);
643 a.translate (Offset (-w, -w * dydx));
644 for (int j = 0; j < lhalfs; j++)
647 b.translate_axis (-get_direction () * dy * (lwholebeams+j), Y_AXIS);
648 leftbeams.add_molecule (b);
654 int rhalfs = here->beam_count (RIGHT) - next->beam_count (LEFT);
655 int rwholebeams= here->beam_count (RIGHT) <? next->beam_count (LEFT) ;
657 Real w = next->hpos_f () - here->hpos_f ();
658 Molecule a = lookup_l ()->beam (dydx, w + stemdx, thick);
659 a.translate_axis( - stemdx/2, X_AXIS);
663 SCM gap = get_elt_property ("beam-gap");
664 if (gh_number_p (gap))
666 int gap_i = gh_scm2int ( (gap));
667 int nogap = rwholebeams - gap_i;
669 for (; j < nogap; j++)
672 b.translate_axis (-get_direction () * dy * j, Y_AXIS);
673 rightbeams.add_molecule (b);
675 // TODO: notehead widths differ for different types
678 a = lookup_l ()->beam (dydx, w + stemdx, thick);
681 for (; j < rwholebeams; j++)
684 if (!here->invisible_b ())
685 b.translate (Offset (gap_f, -get_direction () * dy * j));
687 b.translate (Offset (0, -get_direction () * dy * j));
688 rightbeams.add_molecule (b);
693 a = lookup_l ()->beam (dydx, w, thick);
695 for (; j < rwholebeams + rhalfs; j++)
698 b.translate_axis (-get_direction () * dy * j, Y_AXIS);
699 rightbeams.add_molecule (b);
703 leftbeams.add_molecule (rightbeams);
706 Does beam quanting think of the asymetry of beams?
707 Refpoint is on bottom of symbol. (FIXTHAT) --hwn.
714 Beam::do_brew_molecule_p () const
716 Molecule *mol_p = new Molecule;
720 Real x0 = first_visible_stem ()->hpos_f ();
721 Real dx = last_visible_stem ()->hpos_f () - x0;
722 Real dydx = get_real ("height")/dx;
723 Real y = get_real ("y-position");
724 for (int j=0; j <stem_count (); j++)
727 Stem * prev = (j > 0)? stem (j-1) : 0;
728 Stem * next = (j < stem_count ()-1) ? stem (j+1) :0;
730 Molecule sb = stem_beams (i, next, prev);
731 Real x = i->hpos_f ()-x0;
732 sb.translate (Offset (x, x * dydx + y));
733 mol_p->add_molecule (sb);
735 mol_p->translate_axis (x0
736 - spanned_drul_[LEFT]->relative_coordinate (0, X_AXIS), X_AXIS);
742 Beam::forced_stem_count () const
745 for (int i=0; i < stem_count (); i++)
749 if (s->invisible_b ())
752 if (((int)s->chord_start_f ())
753 && (s->get_direction () != s->get_default_dir ()))
762 TODO: Fix this class. This is wildly inefficient.
763 And it sux. Yet another array/list 'interface'.
766 Beam::stem (int i) const
768 return Group_interface__extract_elements ((Beam*) this, (Stem*) 0, "stems")[i];
772 Beam::stem_count () const
774 Group_interface gi (this, "stems");
779 Beam::stem_top () const
781 SCM s = get_elt_property ("stems");
783 return gh_pair_p (s) ? dynamic_cast<Stem*> (unsmob_element (gh_car (s))) : 0;
785 //Group_interface__extract_elements ((Beam*) this, (Stem*) 0, "stems")[stem_count () - 1];
790 Beam::visible_stem_count () const
793 for (int i = 0; i < stem_count (); i++)
795 if (!stem (i)->invisible_b ())
802 Beam::first_visible_stem () const
804 for (int i = 0; i < stem_count (); i++)
807 if (!s->invisible_b ())
817 Beam::last_visible_stem () const
819 for (int i = stem_count (); i > 0; i--)
821 Stem* s = stem (i - 1);
822 if (!s->invisible_b ())