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)->head_positions()[get_direction ()])
211 + (int)calc_interstaff_dist (stem (i-1), this);
212 int r_y = (int)(stem (i)->head_positions()[get_direction ()])
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)->head_positions()[get_direction ()])
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 Staff_symbol_referencer_interface st (this);
321 Real half_space = st.staff_space () / 2;
322 if (y_shift > half_space / 4)
327 for significantly lengthened or shortened stems,
328 request quanting the other way.
331 if (abs (y_shift) > half_space / 2)
332 quant_dir = sign (y_shift) * get_direction ();
333 y = quantise_y_f (y, dy, quant_dir);
334 set_stem_length (y, dy);
337 set_elt_property ("y-position", gh_double2scm (y));
342 See Documentation/tex/fonts.doc
345 Beam::calc_position_and_height (Real* y, Real* dy) const
348 if (visible_stem_count () <= 1)
351 Real first_ideal = first_visible_stem ()->calc_stem_info ().idealy_f_;
352 if (first_ideal == last_visible_stem ()->calc_stem_info ().idealy_f_)
360 Real x0 = first_visible_stem ()->hpos_f ();
361 for (int i=0; i < stem_count (); i++)
364 if (s->invisible_b ())
366 ls.input.push (Offset (s->hpos_f () - x0,
367 s->calc_stem_info ().idealy_f_));
370 ls.minimise (dydx, *y); // duh, takes references
372 Real dx = last_visible_stem ()->hpos_f () - x0;
377 Beam::suspect_slope_b (Real y, Real dy) const
380 steep slope running against lengthened stem is suspect
382 Real first_ideal = first_visible_stem ()->calc_stem_info ().idealy_f_;
383 Real last_ideal = last_visible_stem ()->calc_stem_info ().idealy_f_;
384 Real lengthened = paper_l ()->get_var ("beam_lengthened");
385 Real steep = paper_l ()->get_var ("beam_steep_slope");
387 Real dx = last_visible_stem ()->hpos_f () - first_visible_stem ()->hpos_f ();
390 if (((y - first_ideal > lengthened) && (dydx > steep))
391 || ((y + dy - last_ideal > lengthened) && (dydx < -steep)))
399 This neat trick is by Werner Lemberg,
400 damped = tanh (slope)
401 corresponds with some tables in [Wanske]
404 Beam::calc_slope_damping_f (Real dy) const
406 SCM damp = get_elt_property ("damping"); // remove?
407 int damping = 1; // ugh.
408 if (gh_number_p (damp))
409 damping = gh_scm2int (damp);
413 Real dx = last_visible_stem ()->hpos_f ()
414 - first_visible_stem ()->hpos_f ();
416 dydx = 0.6 * tanh (dydx) / damping;
423 Beam::calc_stem_y_f (Stem* s, Real y, Real dy) const
425 Real thick = gh_scm2double (get_elt_property ("beam-thickness"));
426 int beam_multiplicity = get_multiplicity ();
427 int stem_multiplicity = (s->flag_i () - 2) >? 0;
429 Real interbeam_f = paper_l ()->interbeam_f (beam_multiplicity);
430 Real x0 = first_visible_stem ()->hpos_f ();
431 Real dx = last_visible_stem ()->hpos_f () - x0;
432 Real stem_y = (s->hpos_f () - x0) / dx * dy + y;
435 if (get_direction () != s->get_direction ())
437 stem_y -= get_direction ()
438 * (thick / 2 + (beam_multiplicity - 1 - stem_multiplicity))
441 Staff_symbol_referencer_interface me (s);
442 Staff_symbol_referencer_interface last (last_visible_stem ());
444 if ((s != first_visible_stem ())
445 && me.staff_symbol_l () != last.staff_symbol_l ())
446 stem_y += get_direction ()
447 * (beam_multiplicity - stem_multiplicity) * interbeam_f;
453 Beam::check_stem_length_f (Real y, Real dy) const
457 for (int i=0; i < stem_count (); i++)
460 if (s->invisible_b ())
463 Real stem_y = calc_stem_y_f (s, y, dy);
465 stem_y *= get_direction ();
466 Stem_info info = s->calc_stem_info ();
468 if (stem_y > info.maxy_f_)
469 shorten = shorten <? info.maxy_f_ - stem_y;
471 if (stem_y < info.miny_f_)
472 lengthen = lengthen >? info.miny_f_ - stem_y;
475 if (lengthen && shorten)
476 warning (_ ("weird beam vertical offset"));
478 /* when all stems are too short, normal stems win */
480 return shorten * get_direction ();
482 return lengthen * get_direction ();
486 Beam::set_stem_length (Real y, Real dy)
488 Staff_symbol_referencer_interface st (this);
489 Real half_space = st.staff_space ()/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)) / half_space);
504 [Ross] (simplification of)
505 Try to set dy complying with:
507 - thick / 2 + staffline_f / 2
508 - thick + 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 staff_space = st.staff_space ();
524 Real staffline_f = paper_l ()->get_var ("stafflinethickness");
525 Real thick = gh_scm2double (get_elt_property ("beam-thickness"));;
527 Array<Real> allowed_fraction (3);
528 allowed_fraction[0] = 0;
529 allowed_fraction[1] = (thick / 2 + staffline_f / 2);
530 allowed_fraction[2] = (thick + staffline_f);
532 allowed_fraction.push (staff_space);
533 Interval iv = quantise_iv (allowed_fraction, abs (dy));
534 Real q = (abs (dy) - iv[SMALLER] <= iv[BIGGER] - abs (dy))
538 return q * sign (dy);
542 Prevent interference from stafflines and beams.
543 See Documentation/tex/fonts.doc
545 TODO: get allowed-positions as scm list (aarg: from paper block)
548 Beam::quantise_y_f (Real y, Real dy, int quant_dir)
551 We only need to quantise the (left) y-position of the beam,
552 since dy is quantised too.
553 if extend_b then stems must *not* get shorter
555 SCM s = get_elt_property ("slope-quantisation");
556 if (s == ly_symbol2scm ("none"))
560 ----------------------------------------------------------
564 --------------########------------------------------------
567 hang straddle sit inter hang
570 Staff_symbol_referencer_interface st (this);
571 Real staff_space = st.staff_space ();
572 Real staffline_f = paper_l ()->get_var ("stafflinethickness");
573 Real thick = gh_scm2double (get_elt_property ("beam-thickness"));;
576 Real sit = thick / 2 - staffline_f / 2;
577 Real hang = staff_space - thick / 2 + staffline_f / 2;
580 Put all allowed positions into an array.
581 Whether a position is allowed or not depends on
582 strictness of quantisation, multiplicity and direction.
584 For simplicity, we'll assume dir = UP and correct if
585 dir = DOWN afterwards.
588 int multiplicity = get_multiplicity ();
591 Array<Real> allowed_position;
592 if (s == ly_symbol2scm ("normal"))
594 if ((multiplicity <= 2) || (abs (dy) >= staffline_f / 2))
595 allowed_position.push (straddle);
596 if ((multiplicity <= 1) || (abs (dy) >= staffline_f / 2))
597 allowed_position.push (sit);
598 allowed_position.push (hang);
600 else if (s == ly_symbol2scm ("traditional"))
602 // TODO: check and fix TRADITIONAL
603 if ((multiplicity <= 2) || (abs (dy) >= staffline_f / 2))
604 allowed_position.push (straddle);
605 if ((multiplicity <= 1) && (dy <= staffline_f / 2))
606 allowed_position.push (sit);
607 if (dy >= -staffline_f / 2)
608 allowed_position.push (hang);
611 allowed_position.push (staff_space);
612 Real up_y = get_direction () * y;
613 Interval iv = quantise_iv (allowed_position, up_y);
615 Real q = up_y - iv[SMALLER] <= iv[BIGGER] - up_y
616 ? iv[SMALLER] : iv[BIGGER];
618 q = iv[(Direction)quant_dir];
620 return q * get_direction ();
624 Beam::set_beaming (Beaming_info_list *beaming)
627 for (int i=0; i < stem_count (); i++)
631 if (stem (i)->beam_count (d) == 0)
632 stem (i)->set_beaming ( beaming->infos_.elem (i).beams_i_drul_[d],d);
634 while (flip (&d) != LEFT);
641 beams to go with one stem.
647 Beam::stem_beams (Stem *here, Stem *next, Stem *prev) const
649 if ((next && !(next->hpos_f () > here->hpos_f ())) ||
650 (prev && !(prev->hpos_f () < here->hpos_f ())))
651 programming_error ("Beams are not left-to-right");
653 Real staffline_f = paper_l ()->get_var ("stafflinethickness");
654 int multiplicity = get_multiplicity ();
657 Real interbeam_f = paper_l ()->interbeam_f (multiplicity);
658 Real thick = gh_scm2double (get_elt_property ("beam-thickness"));;
660 Real dy = interbeam_f;
661 Real stemdx = staffline_f;
663 Real dx = last_visible_stem ()->hpos_f () - first_visible_stem ()->hpos_f ();
664 Real dydx = get_real ("height")/dx;
671 if (!here->first_head ())
673 else if (here->type_i ()== 1)
674 nw_f = paper_l ()->get_var ("wholewidth");
675 else if (here->type_i () == 2)
676 nw_f = paper_l ()->get_var ("notewidth") * 0.8;
678 nw_f = paper_l ()->get_var ("quartwidth");
680 /* half beams extending to the left. */
683 int lhalfs= lhalfs = here->beam_count (LEFT) - prev->beam_count (RIGHT);
684 int lwholebeams= here->beam_count (LEFT) <? prev->beam_count (RIGHT) ;
686 Half beam should be one note-width,
687 but let's make sure two half-beams never touch
689 Real w = here->hpos_f () - prev->hpos_f ();
692 if (lhalfs) // generates warnings if not
693 a = lookup_l ()->beam (dydx, w, thick);
694 a.translate (Offset (-w, -w * dydx));
695 for (int j = 0; j < lhalfs; j++)
698 b.translate_axis (-get_direction () * dy * (lwholebeams+j), Y_AXIS);
699 leftbeams.add_molecule (b);
705 int rhalfs = here->beam_count (RIGHT) - next->beam_count (LEFT);
706 int rwholebeams= here->beam_count (RIGHT) <? next->beam_count (LEFT) ;
708 Real w = next->hpos_f () - here->hpos_f ();
709 Molecule a = lookup_l ()->beam (dydx, w + stemdx, thick);
710 a.translate_axis( - stemdx/2, X_AXIS);
714 SCM gap = get_elt_property ("beam-gap");
715 if (gh_number_p (gap))
717 int gap_i = gh_scm2int ( (gap));
718 int nogap = rwholebeams - gap_i;
720 for (; j < nogap; j++)
723 b.translate_axis (-get_direction () * dy * j, Y_AXIS);
724 rightbeams.add_molecule (b);
726 // TODO: notehead widths differ for different types
729 a = lookup_l ()->beam (dydx, w + stemdx, thick);
732 for (; j < rwholebeams; j++)
735 if (!here->invisible_b ())
736 b.translate (Offset (gap_f, -get_direction () * dy * j));
738 b.translate (Offset (0, -get_direction () * dy * j));
739 rightbeams.add_molecule (b);
744 a = lookup_l ()->beam (dydx, w, thick);
746 for (; j < rwholebeams + rhalfs; j++)
749 b.translate_axis (-get_direction () * dy * j, Y_AXIS);
750 rightbeams.add_molecule (b);
754 leftbeams.add_molecule (rightbeams);
757 Does beam quanting think of the asymetry of beams?
758 Refpoint is on bottom of symbol. (FIXTHAT) --hwn.
765 Beam::do_brew_molecule_p () const
767 Molecule *mol_p = new Molecule;
771 Real x0 = first_visible_stem ()->hpos_f ();
772 Real dx = last_visible_stem ()->hpos_f () - x0;
773 Real dydx = get_real ("height")/dx;
774 Real y = get_real ("y-position");
775 for (int j=0; j <stem_count (); j++)
778 Stem * prev = (j > 0)? stem (j-1) : 0;
779 Stem * next = (j < stem_count ()-1) ? stem (j+1) :0;
781 Molecule sb = stem_beams (i, next, prev);
782 Real x = i->hpos_f ()-x0;
783 sb.translate (Offset (x, x * dydx + y));
784 mol_p->add_molecule (sb);
786 mol_p->translate_axis (x0
787 - spanned_drul_[LEFT]->relative_coordinate (0, X_AXIS), X_AXIS);
793 Beam::forced_stem_count () const
796 for (int i=0; i < stem_count (); i++)
800 if (s->invisible_b ())
803 if (((int)s->chord_start_f ())
804 && (s->get_direction () != s->get_default_dir ()))
813 TODO: Fix this class. This is wildly inefficient.
814 And it sux. Yet another array/list 'interface'.
817 Beam::stem (int i) const
819 return Group_interface__extract_elements ((Beam*) this, (Stem*) 0, "stems")[i];
823 Beam::stem_count () const
825 Group_interface gi (this, "stems");
830 Beam::stem_top () const
832 SCM s = get_elt_property ("stems");
834 return gh_pair_p (s) ? dynamic_cast<Stem*> (unsmob_element (gh_car (s))) : 0;
836 //Group_interface__extract_elements ((Beam*) this, (Stem*) 0, "stems")[stem_count () - 1];
841 Beam::visible_stem_count () const
844 for (int i = 0; i < stem_count (); i++)
846 if (!stem (i)->invisible_b ())
853 Beam::first_visible_stem () const
855 for (int i = 0; i < stem_count (); i++)
858 if (!s->invisible_b ())
868 Beam::last_visible_stem () const
870 for (int i = stem_count (); i > 0; i--)
872 Stem* s = stem (i - 1);
873 if (!s->invisible_b ())