2 beam.cc -- implement Beam
4 source file of the GNU LilyPond music typesetter
6 (c) 1997--2002 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7 Jan Nieuwenhuizen <janneke@gnu.org>
18 * Remove #'direction from beam. A beam has no direction per se.
19 It may only set directions for stems.
26 #include <math.h> // tanh.
28 #include "molecule.hh"
29 #include "directional-element-interface.hh"
33 #include "least-squares.hh"
35 #include "paper-def.hh"
37 #include "group-interface.hh"
38 #include "staff-symbol-referencer.hh"
42 #include "text-item.hh" // debug output.
43 #include "font-interface.hh" // debug output.
48 shrink_extra_weight (Real x)
50 return fabs (x) * ((x < 0) ? 1.5 : 1.0);
54 Beam::add_stem (Grob *me, Grob *s)
56 Pointer_group_interface::add_grob (me, ly_symbol2scm ("stems"), s);
58 s->add_dependency (me);
60 assert (!Stem::beam_l (s));
61 s->set_grob_property ("beam", me->self_scm ());
63 add_bound_item (dynamic_cast<Spanner*> (me), dynamic_cast<Item*> (s));
67 Beam::get_interbeam (Grob *me)
69 Real slt = me->paper_l ()->get_var ("stafflinethickness");
70 Real ss = Staff_symbol_referencer::staff_space (me);
71 Real thickness = gh_scm2double (me->get_grob_property ("thickness"))
74 int multiplicity = get_multiplicity (me);
75 Real interbeam = multiplicity < 4
76 ? (2*ss + slt - thickness) / 2.0
77 : (3*ss + slt - thickness) / 3.0;
83 Beam::get_multiplicity (Grob *me)
86 for (SCM s = me->get_grob_property ("stems"); gh_pair_p (s); s = ly_cdr (s))
88 Grob *sc = unsmob_grob (ly_car (s));
90 if (Stem::has_interface (sc))
91 m = m >? Stem::beam_count (sc, LEFT) >? Stem::beam_count (sc, RIGHT);
96 /* After pre-processing all directions should be set.
97 Several post-processing routines (stem, slur, script) need stem/beam
99 Currenly, this means that beam has set all stem's directions.
100 [Alternatively, stems could set its own directions, according to
101 their beam, during 'final-pre-processing'.] */
102 MAKE_SCHEME_CALLBACK (Beam, before_line_breaking, 1);
104 Beam::before_line_breaking (SCM smob)
106 Grob *me = unsmob_grob (smob);
108 /* Beams with less than 2 two stems don't make much sense, but could happen
113 For a beam that only has one stem, we try to do some disappearance magic:
114 we revert the flag, and move on to The Eternal Engraving Fields. */
116 int count = visible_stem_count (me);
119 me->warning (_ ("beam has less than two visible stems"));
121 SCM stems = me->get_grob_property ("stems");
122 if (scm_ilength (stems) == 1)
124 me->warning (_ ("Beam has less than two stems. Removing beam."));
126 unsmob_grob (gh_car (stems))->remove_grob_property ("beam");
129 return SCM_UNSPECIFIED;
131 else if (scm_ilength (stems) == 0)
134 return SCM_UNSPECIFIED;
139 if (!Directional_element_interface::get (me))
140 Directional_element_interface::set (me, get_default_dir (me));
142 consider_auto_knees (me);
143 set_stem_directions (me);
144 set_stem_shorten (me);
150 Beam::get_default_dir (Grob *me)
152 Drul_array<int> total;
153 total[UP] = total[DOWN] = 0;
154 Drul_array<int> count;
155 count[UP] = count[DOWN] = 0;
158 Link_array<Item> stems=
159 Pointer_group_interface__extract_grobs (me, (Item*)0, "stems");
161 for (int i=0; i <stems.size (); i++)
164 Direction sd = Directional_element_interface::get (s);
165 int current = sd ? (1 + d * sd)/2
166 : Stem::get_center_distance (s, (Direction)-d);
173 } while (flip (&d) != DOWN);
175 SCM func = me->get_grob_property ("dir-function");
176 SCM s = gh_call2 (func,
177 gh_cons (gh_int2scm (count[UP]),
178 gh_int2scm (count[DOWN])),
179 gh_cons (gh_int2scm (total[UP]),
180 gh_int2scm (total[DOWN])));
182 if (gh_number_p (s) && gh_scm2int (s))
185 /* If dir is not determined: get default */
186 return to_dir (me->get_grob_property ("neutral-direction"));
190 /* Set all stems with non-forced direction to beam direction.
191 Urg: non-forced should become `without/with unforced' direction,
192 once stem gets cleaned-up. */
194 Beam::set_stem_directions (Grob *me)
196 Link_array<Item> stems
197 =Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");
198 Direction d = Directional_element_interface::get (me);
200 for (int i=0; i <stems.size (); i++)
203 SCM force = s->remove_grob_property ("dir-forced");
204 if (!gh_boolean_p (force) || !gh_scm2bool (force))
205 Directional_element_interface::set (s, d);
209 /* Simplistic auto-knees; only consider vertical gap between two
212 `Forced' stem directions are ignored. If you don't want auto-knees,
213 don't set, or unset auto-knee-gap. */
215 Beam::consider_auto_knees (Grob *me)
217 SCM scm = me->get_grob_property ("auto-knee-gap");
219 if (gh_number_p (scm))
223 Real staff_space = Staff_symbol_referencer::staff_space (me);
224 Real gap = gh_scm2double (scm) / staff_space;
226 Direction d = Directional_element_interface::get (me);
227 Link_array<Item> stems=
228 Pointer_group_interface__extract_grobs (me, (Item*)0, "stems");
230 Grob *common = me->common_refpoint (stems[0], Y_AXIS);
231 for (int i=1; i < stems.size (); i++)
232 if (!Stem::invisible_b (stems[i]))
233 common = common->common_refpoint (stems[i], Y_AXIS);
236 for (int i=1; i < stems.size (); i++)
238 if (!Stem::invisible_b (stems[i-1]))
240 if (Stem::invisible_b (stems[l]))
242 if (Stem::invisible_b (stems[i]))
245 Real left = Stem::extremal_heads (stems[l])[d]
246 ->relative_coordinate (common, Y_AXIS);
247 Real right = Stem::extremal_heads (stems[i])[-d]
248 ->relative_coordinate (common, Y_AXIS);
250 Real dy = right - left;
254 knee_y = (right + left) / 2;
262 for (int i=0; i < stems.size (); i++)
264 if (Stem::invisible_b (stems[i]))
267 Real y = Stem::extremal_heads (stems[i])[d]
268 ->relative_coordinate (common, Y_AXIS);
270 Directional_element_interface::set (s, y < knee_y ? UP : DOWN);
271 s->set_grob_property ("dir-forced", SCM_BOOL_T);
277 /* Set stem's shorten property if unset.
279 take some y-position (chord/beam/nearest?) into account
280 scmify forced-fraction */
282 Beam::set_stem_shorten (Grob *m)
284 Spanner*me = dynamic_cast<Spanner*> (m);
286 Real forced_fraction = forced_stem_count (me) / visible_stem_count (me);
288 int multiplicity = get_multiplicity (me);
290 SCM shorten = me->get_grob_property ("beamed-stem-shorten");
291 if (shorten == SCM_EOL)
294 int sz = scm_ilength (shorten);
296 Real staff_space = Staff_symbol_referencer::staff_space (me);
297 SCM shorten_elt = scm_list_ref (shorten,
298 gh_int2scm (multiplicity <? (sz - 1)));
299 Real shorten_f = gh_scm2double (shorten_elt) * staff_space;
301 /* your similar cute comment here */
302 shorten_f *= forced_fraction;
304 me->set_grob_property ("shorten", gh_double2scm (shorten_f));
307 /* Call list of y-dy-callbacks, that handle setting of
308 grob-properties y, dy.
310 User may set grob-properties: y-position-hs and height-hs
311 (to be fixed) that override the calculated y and dy.
313 Because y and dy cannot be calculated and quanted separately, we
314 always calculate both, then check for user override. */
315 MAKE_SCHEME_CALLBACK (Beam, after_line_breaking, 1);
317 Beam::after_line_breaking (SCM smob)
319 Grob *me = unsmob_grob (smob);
321 /* Copy to mutable list. */
322 SCM s = ly_deep_copy (me->get_grob_property ("positions"));
323 me->set_grob_property ("positions", s);
325 if (ly_car (s) != SCM_BOOL_F)
326 return SCM_UNSPECIFIED;
328 SCM callbacks = me->get_grob_property ("position-callbacks");
329 for (SCM i = callbacks; gh_pair_p (i); i = ly_cdr (i))
330 gh_call1 (ly_car (i), smob);
332 return SCM_UNSPECIFIED;
344 - Make all demerits customisable
345 - One sensible check per demerit
346 - Add demerits for quants per se, as to forbid a specific quant
349 MAKE_SCHEME_CALLBACK (Beam, quanting, 1);
351 Beam::quanting (SCM smob)
353 Grob *me = unsmob_grob (smob);
355 SCM s = me->get_grob_property ("positions");
356 Real yl = gh_scm2double (gh_car (s));
357 Real yr = gh_scm2double (gh_cdr (s));
359 Real ss = Staff_symbol_referencer::staff_space (me);
360 Real thickness = gh_scm2double (me->get_grob_property ("thickness")) / ss;
361 Real slt = me->paper_l ()->get_var ("stafflinethickness") / ss;
364 Real sit = (thickness - slt) / 2;
366 Real hang = 1.0 - (thickness - slt) / 2;
367 Real quants [] = {straddle, sit, inter, hang };
369 int num_quants = int (sizeof (quants)/sizeof (Real));
373 const int REGION_SIZE = 3;
374 // -> result indexes between 70 and 575
375 for (int i = -REGION_SIZE ; i < REGION_SIZE; i++)
376 for (int j = 0; j < num_quants; j++)
378 quantsl.push (i + quants[j] + int (yl));
379 quantsr.push (i + quants[j] + int (yr));
382 Array<Quant_score> qscores;
384 for (int l =0; l < quantsl.size (); l++)
385 for (int r =0; r < quantsr.size (); r++)
396 SCM score_funcs = me->get_grob_property ("quant-score-functions");
397 for (SCM s = score_funcs; gh_pair_p (s); s = gh_cdr (s))
400 for (int i = qscores.size (); i--;)
403 // if (qscores[i].demerits < 1000)
404 if (qscores[i].demerits < 100)
406 SCM score = gh_call3 (f,
408 gh_double2scm (qscores[i].yl),
409 gh_double2scm (qscores[i].yr));
411 qscores[i].demerits += gh_scm2double (score);
418 for (int i = qscores.size (); i--;)
420 if (qscores[i].demerits < best)
422 best = qscores [i].demerits ;
428 me->set_grob_property ("positions",
429 gh_cons (gh_double2scm (qscores[best_idx].yl),
430 gh_double2scm (qscores[best_idx].yr))
436 me->set_grob_property ("quant-score",
437 gh_double2scm (qscores[best_idx].demerits));
438 me->set_grob_property ("best-idx", gh_int2scm (best_idx));
441 return SCM_UNSPECIFIED;
444 MAKE_SCHEME_CALLBACK (Beam, score_slopes_dy, 3);
446 Beam::score_slopes_dy (SCM smob, SCM syl, SCM syr)
448 Grob*me = unsmob_grob (smob);
449 Real yl = gh_scm2double (syl);
450 Real yr = gh_scm2double (syr);
453 SCM sdy = me->get_grob_property ("least-squares-dy");
454 SCM posns = me->get_grob_property ("positions");
456 Real dy_mus = gh_number_p (sdy) ? gh_scm2double (sdy) : 0.0;
457 Real dy_damp = - gh_scm2double (gh_car (posns)) + gh_scm2double (gh_cdr (posns));
460 if (sign (dy_damp) != sign (dy))
465 dem += 400* (0 >? (fabs (dy) - fabs (dy_mus)));
468 dem += shrink_extra_weight (fabs (dy_damp) - fabs (dy))* 10;
469 return gh_double2scm (dem);
472 MAKE_SCHEME_CALLBACK (Beam, score_stem_lengths, 3);
474 Beam::score_stem_lengths (SCM smob, SCM syl, SCM syr)
476 Grob*me = unsmob_grob (smob);
477 Real yl = gh_scm2double (syl);
478 Real yr = gh_scm2double (syr);
480 Link_array<Item> stems=
481 Pointer_group_interface__extract_grobs (me, (Item*)0, "stems");
483 Real demerit_score = 0.0 ;
485 for (int i=0; i < stems.size (); i++)
488 if (Stem::invisible_b (s))
491 Real current_y = calc_stem_y (me, s, Interval (yl, yr));
492 Stem_info info = Stem::calc_stem_info (s);
493 Direction d = Directional_element_interface::get (s);
495 demerit_score += 500 * ( 0 >? (info.min_y - d * current_y));
496 demerit_score += 500 * ( 0 >? (d * current_y - info.max_y));
498 demerit_score += 5 * shrink_extra_weight (d * current_y - info.ideal_y);
501 demerit_score *= 2.0 /stems.size ();
503 return gh_double2scm (demerit_score);
509 return x - floor (x);
514 MAKE_SCHEME_CALLBACK (Beam, score_forbidden_quants, 3);
516 Beam::score_forbidden_quants (SCM smob, SCM syl, SCM syr)
518 Grob*me = unsmob_grob (smob);
519 Real yl = gh_scm2double (syl);
520 Real yr = gh_scm2double (syr);
522 Real rad = Staff_symbol_referencer::staff_radius (me);
524 if (fabs (yl) < rad && fabs ( my_modf (yl) - 0.5) < 1e-3)
526 if (fabs (yr) < rad && fabs ( my_modf (yr) - 0.5) < 1e-3)
530 int multiplicity = get_multiplicity (me);
531 // todo: use multiplicity of outer stems.
532 if (multiplicity >= 2)
534 Real slt = me->paper_l ()->get_var ("stafflinethickness");
535 Real ss = Staff_symbol_referencer::staff_space (me);
536 Real thickness = gh_scm2double (me->get_grob_property ("thickness"))
539 Real interbeam = multiplicity < 4
540 ? (2*ss + slt - thickness) / 2.0
541 : (3*ss + slt - thickness) / 3.0;
544 Real sit = (thickness - slt) / 2;
546 Real hang = 1.0 - (thickness - slt) / 2;
548 Direction dir = Directional_element_interface::get (me);
549 if (fabs (yl - dir * interbeam) < rad
550 && fabs (my_modf (yl) - inter) < 1e-3)
552 if (fabs (yr - dir * interbeam) < rad
553 && fabs (my_modf (yr) - inter) < 1e-3)
556 // hmm, without Interval/Drul_array, you get ~ 4x same code...
557 if (fabs (yl - dir * interbeam) < rad + inter)
559 if (dir == UP && dy <= 1e-3
560 && fabs (my_modf (yl) - sit) < 1e-3)
563 if (dir == DOWN && dy >= 1e-3
564 && fabs (my_modf (yl) - hang) < 1e-3)
568 if (fabs (yr - dir * interbeam) < rad + inter)
570 if (dir == UP && dy >= 1e-3
571 && fabs (my_modf (yr) - sit) < 1e-3)
574 if (dir == DOWN && dy <= 1e-3
575 && fabs (my_modf (yr) - hang) < 1e-3)
579 if (multiplicity >= 3)
581 if (fabs (yl - 2 * dir * interbeam) < rad + inter)
583 if (dir == UP && dy <= 1e-3
584 && fabs (my_modf (yl) - straddle) < 1e-3)
587 if (dir == DOWN && dy >= 1e-3
588 && fabs (my_modf (yl) - straddle) < 1e-3)
592 if (fabs (yr - 2 * dir * interbeam) < rad + inter)
594 if (dir == UP && dy >= 1e-3
595 && fabs (my_modf (yr) - straddle) < 1e-3)
598 if (dir == DOWN && dy <= 1e-3
599 && fabs (my_modf (yr) - straddle) < 1e-3)
605 return gh_double2scm ( dem);
610 MAKE_SCHEME_CALLBACK (Beam, least_squares, 1);
612 Beam::least_squares (SCM smob)
614 Grob *me = unsmob_grob (smob);
616 int count = visible_stem_count (me);
621 me->set_grob_property ("positions", ly_interval2scm (pos));
622 return SCM_UNSPECIFIED;
625 Direction dir = Directional_element_interface::get (me);
627 Interval ideal (Stem::calc_stem_info (first_visible_stem (me)).ideal_y,
628 Stem::calc_stem_info (last_visible_stem (me)).ideal_y);
632 Interval chord (Stem::chord_start_f (first_visible_stem (me)),
633 Stem::chord_start_f (last_visible_stem (me)));
637 TODO : use scoring for this.
639 complicated, because we take stem-info.ideal for determining
643 /* Make simple beam on middle line have small tilt */
644 if (!ideal[LEFT] && chord.delta () && count == 2)
646 Direction d = (Direction) (sign (chord.delta ()) * dir);
647 pos[d] = gh_scm2double (me->get_grob_property ("thickness")) / 2
660 Array<Offset> ideals;
662 // ugh -> use commonx
663 Real x0 = first_visible_stem (me)->relative_coordinate (0, X_AXIS);
664 Link_array<Item> stems=
665 Pointer_group_interface__extract_grobs (me, (Item*)0, "stems");
667 for (int i=0; i < stems.size (); i++)
670 if (Stem::invisible_b (s))
672 ideals.push (Offset (s->relative_coordinate (0, X_AXIS) - x0,
673 Stem::calc_stem_info (s).ideal_y));
677 minimise_least_squares (&dydx, &y, ideals);
679 Real dx = last_visible_stem (me)->relative_coordinate (0, X_AXIS) - x0;
681 me->set_grob_property ("least-squares-dy", gh_double2scm (dy * dir));
683 pos = Interval (y*dir, (y+dy) * dir);
686 me->set_grob_property ("positions", ly_interval2scm (pos));
687 return SCM_UNSPECIFIED;
690 MAKE_SCHEME_CALLBACK (Beam, check_concave, 1);
692 Beam::check_concave (SCM smob)
694 Grob *me = unsmob_grob (smob);
696 Link_array<Item> stems =
697 Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");
699 for (int i = 0; i < stems.size ();)
701 if (Stem::invisible_b (stems[i]))
707 if (stems.size () < 3)
708 return SCM_UNSPECIFIED;
710 /* Concaveness try #2: Sum distances of inner noteheads that
711 fall outside the interval of the two outer noteheads */
713 Interval iv (Stem::chord_start_f (stems[0]),
714 Stem::chord_start_f (stems.top ()));
716 if (iv[MAX] < iv[MIN])
719 for (int i = 1; i < stems.size () - 1; i++)
722 Real f = Stem::chord_start_f (stems[i]);
723 if ((c = f - iv[MAX]) > 0)
725 else if ((c = f - iv[MIN]) < 0)
729 Direction dir = Directional_element_interface::get (me);
732 Real concaveness = concave / (stems.size () - 2);
733 /* ugh: this is the a kludge to get input/regression/beam-concave.ly
734 to behave as baerenreiter. */
735 concaveness /= (stems.size () - 2);
737 Real r = gh_scm2double (me->get_grob_property ("concaveness-threshold"));
739 /* TODO: some sort of damping iso -> plain horizontal */
742 Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
743 Real r = pos.linear_combination (0);
744 me->set_grob_property ("positions", ly_interval2scm (Interval (r, r)));
745 me->set_grob_property ("least-squares-dy", gh_double2scm (0));
748 return SCM_UNSPECIFIED;
751 /* This neat trick is by Werner Lemberg,
752 damped = tanh (slope)
753 corresponds with some tables in [Wanske] CHECKME */
754 MAKE_SCHEME_CALLBACK (Beam, slope_damping, 1);
756 Beam::slope_damping (SCM smob)
758 Grob *me = unsmob_grob (smob);
760 if (visible_stem_count (me) <= 1)
761 return SCM_UNSPECIFIED;
763 SCM s = me->get_grob_property ("damping");
764 int damping = gh_scm2int (s);
768 Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
769 Real dy = pos.delta ();
771 // ugh -> use commonx
772 Real dx = last_visible_stem (me)->relative_coordinate (0, X_AXIS)
773 - first_visible_stem (me)->relative_coordinate (0, X_AXIS);
774 Real dydx = dy && dx ? dy/dx : 0;
775 dydx = 0.6 * tanh (dydx) / damping;
777 Real damped_dy = dydx * dx;
778 pos[LEFT] += (dy - damped_dy) / 2;
779 pos[RIGHT] -= (dy - damped_dy) / 2;
781 me->set_grob_property ("positions", ly_interval2scm (pos));
783 return SCM_UNSPECIFIED;
786 MAKE_SCHEME_CALLBACK (Beam, end_after_line_breaking, 1);
788 Beam::end_after_line_breaking (SCM smob)
790 Grob *me = unsmob_grob (smob);
791 set_stem_lengths (me);
793 return SCM_UNSPECIFIED;
797 Calculate the Y position of the stem-end, given the Y-left, Y-right
798 in POS, and for stem S.
801 Beam::calc_stem_y (Grob *me, Item* s, Interval pos)
803 int beam_multiplicity = get_multiplicity (me);
804 int stem_multiplicity = (Stem::flag_i (s) - 2) >? 0;
806 Real thick = gh_scm2double (me->get_grob_property ("thickness"));
807 Real interbeam = get_interbeam (me);
809 // ugh -> use commonx
810 Real x0 = first_visible_stem (me)->relative_coordinate (0, X_AXIS);
811 Real dx = last_visible_stem (me)->relative_coordinate (0, X_AXIS) - x0;
812 Real dy = pos.delta ();
813 Real stem_y = (dy && dx
814 ? (s->relative_coordinate (0, X_AXIS) - x0) / dx
819 Direction dir = Directional_element_interface::get (me);
820 Direction sdir = Directional_element_interface::get (s);
825 stem_y -= dir * (thick / 2 + (beam_multiplicity - 1) * interbeam);
827 // huh, why not for first visible?
828 if (Staff_symbol_referencer::staff_symbol_l (s)
829 != Staff_symbol_referencer::staff_symbol_l (last_visible_stem (me)))
830 stem_y += Directional_element_interface::get (me)
831 * (beam_multiplicity - stem_multiplicity) * interbeam;
838 Hmm. At this time, beam position and slope are determined. Maybe,
839 stem directions and length should set to relative to the chord's
840 position of the beam. */
842 Beam::set_stem_lengths (Grob *me)
844 Link_array<Item> stems=
845 Pointer_group_interface__extract_grobs (me, (Item*)0, "stems");
847 if (stems.size () <= 1)
850 Grob *common = me->common_refpoint (stems[0], Y_AXIS);
851 for (int i=1; i < stems.size (); i++)
852 if (!Stem::invisible_b (stems[i]))
853 common = common->common_refpoint (stems[i], Y_AXIS);
855 Direction dir = Directional_element_interface::get (me);
856 Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
857 Real staff_space = Staff_symbol_referencer::staff_space (me);
858 Real thick = gh_scm2double (me->get_grob_property ("thickness"));
859 bool ps_testing = to_boolean (ly_symbol2scm ("ps-testing"));
860 for (int i=0; i < stems.size (); i++)
863 if (Stem::invisible_b (s))
866 Real stem_y = calc_stem_y (me, s, pos);
868 // doesn't play well with dvips
870 if (Stem::get_direction (s) == dir)
871 stem_y += Stem::get_direction (s) * thick / 2;
873 /* caution: stem measures in staff-positions */
874 Real id = me->relative_coordinate (common, Y_AXIS)
875 - stems[i]->relative_coordinate (common, Y_AXIS);
876 Stem::set_stemend (s, (stem_y + id) / staff_space * 2);
881 Beam::set_beaming (Grob *me, Beaming_info_list *beaming)
883 Link_array<Grob> stems=
884 Pointer_group_interface__extract_grobs (me, (Grob *)0, "stems");
887 for (int i=0; i < stems.size (); i++)
891 /* Don't overwrite user override (?) */
892 if (Stem::beam_count (stems[i], d) == -1
893 /* Don't set beaming for outside of outer stems */
894 && ! (d == LEFT && i == 0)
895 && ! (d == RIGHT && i == stems.size () -1))
897 int b = beaming->infos_.elem (i).beams_i_drul_[d];
898 Stem::set_beaming (stems[i], b, d);
901 while (flip (&d) != LEFT);
908 beams to go with one stem.
913 Beam::stem_beams (Grob *me, Item *here, Item *next, Item *prev, Real dydx)
915 // ugh -> use commonx
917 && ! (next->relative_coordinate (0, X_AXIS)
918 > here->relative_coordinate (0, X_AXIS)))
920 && ! (prev->relative_coordinate (0, X_AXIS)
921 < here->relative_coordinate (0, X_AXIS))))
922 programming_error ("Beams are not left-to-right");
924 Real thick = gh_scm2double (me->get_grob_property ("thickness"));
925 Real bdy = get_interbeam (me);
931 if (!Stem::first_head (here))
934 int t = Stem::type_i (here);
936 SCM proc = me->get_grob_property ("flag-width-function");
937 SCM result = gh_call1 (proc, gh_int2scm (t));
938 nw_f = gh_scm2double (result);
942 Direction dir = Directional_element_interface::get (me);
944 /* [Tremolo] beams on whole notes may not have direction set? */
946 dir = Directional_element_interface::get (here);
949 /* half beams extending to the left. */
952 int lhalfs= lhalfs = Stem::beam_count (here, LEFT)
953 - Stem::beam_count (prev, RIGHT);
954 int lwholebeams= Stem::beam_count (here, LEFT)
955 <? Stem::beam_count (prev, RIGHT);
957 /* Half beam should be one note-width,
958 but let's make sure two half-beams never touch */
960 // FIXME: TODO (check) stem width / sloped beams
961 Real w = here->relative_coordinate (0, X_AXIS)
962 - prev->relative_coordinate (0, X_AXIS);
963 Real stem_w = gh_scm2double (prev->get_grob_property ("thickness"))
965 * me->paper_l ()->get_var ("stafflinethickness");
969 if (lhalfs) // generates warnings if not
970 a = Lookup::beam (dydx, w + stem_w, thick);
971 a.translate (Offset (-w, -w * dydx));
972 a.translate_axis (-stem_w/2, X_AXIS);
973 for (int j = 0; j < lhalfs; j++)
976 b.translate_axis (-dir * bdy * (lwholebeams+j), Y_AXIS);
977 leftbeams.add_molecule (b);
983 int rhalfs = Stem::beam_count (here, RIGHT)
984 - Stem::beam_count (next, LEFT);
985 int rwholebeams= Stem::beam_count (here, RIGHT)
986 <? Stem::beam_count (next, LEFT);
988 Real w = next->relative_coordinate (0, X_AXIS)
989 - here->relative_coordinate (0, X_AXIS);
991 Real stem_w = gh_scm2double (next->get_grob_property ("thickness"))
993 * me->paper_l ()->get_var ("stafflinethickness");
995 Molecule a = Lookup::beam (dydx, w + stem_w, thick);
996 a.translate_axis (- stem_w/2, X_AXIS);
1000 SCM gap = me->get_grob_property ("gap");
1001 if (gh_number_p (gap))
1003 int gap_i = gh_scm2int ((gap));
1004 int nogap = rwholebeams - gap_i;
1006 for (; j < nogap; j++)
1009 b.translate_axis (-dir * bdy * j, Y_AXIS);
1010 rightbeams.add_molecule (b);
1012 if (Stem::invisible_b (here))
1017 a = Lookup::beam (dydx, w + stem_w, thick);
1020 for (; j < rwholebeams; j++)
1024 if (Stem::invisible_b (here))
1025 // ugh, see chord-tremolo.ly
1026 tx = (-dir + 1) / 2 * nw_f * 1.5 + gap_f/4;
1029 b.translate (Offset (tx, -dir * bdy * j));
1030 rightbeams.add_molecule (b);
1035 a = Lookup::beam (dydx, w, thick);
1037 for (; j < rwholebeams + rhalfs; j++)
1040 b.translate_axis (- dir * bdy * j, Y_AXIS);
1041 rightbeams.add_molecule (b);
1045 leftbeams.add_molecule (rightbeams);
1047 /* Does beam quanting think of the asymetry of beams?
1048 Refpoint is on bottom of symbol. (FIXTHAT) --hwn. */
1053 MAKE_SCHEME_CALLBACK (Beam, brew_molecule, 1);
1055 Beam::brew_molecule (SCM smob)
1057 Grob *me =unsmob_grob (smob);
1060 if (!gh_pair_p (me->get_grob_property ("stems")))
1063 Link_array<Item>stems =
1064 Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");
1065 if (visible_stem_count (me))
1067 // ugh -> use commonx
1068 x0 = first_visible_stem (me)->relative_coordinate (0, X_AXIS);
1069 dx = last_visible_stem (me)->relative_coordinate (0, X_AXIS) - x0;
1073 x0 = stems[0]->relative_coordinate (0, X_AXIS);
1074 dx = stems.top ()->relative_coordinate (0, X_AXIS) - x0;
1077 Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
1078 Real dy = pos.delta ();
1079 Real dydx = dy && dx ? dy/dx : 0;
1081 for (int i=0; i < stems.size (); i++)
1083 Item *item = stems[i];
1084 Item *prev = (i > 0)? stems[i-1] : 0;
1085 Item *next = (i < stems.size ()-1) ? stems[i+1] :0;
1087 Molecule sb = stem_beams (me, item, next, prev, dydx);
1088 Real x = item->relative_coordinate (0, X_AXIS) - x0;
1089 sb.translate (Offset (x, x * dydx + pos[LEFT]));
1090 mol.add_molecule (sb);
1093 mol.translate_axis (x0
1094 - dynamic_cast<Spanner*> (me)
1095 ->get_bound (LEFT)->relative_coordinate (0, X_AXIS),
1101 This code prints the demerits for each beam. Perhaps this
1102 should be switchable for those who want to twiddle with the
1108 str += to_str (gh_scm2int (me->get_grob_property ("best-idx")));
1111 str += to_str (gh_scm2double (me->get_grob_property ("quant-score")),
1114 SCM properties = Font_interface::font_alist_chain (me);
1116 Molecule tm = Text_item::text2molecule (me, gh_str02scm (str.ch_C ()), properties);
1117 mol.add_at_edge (Y_AXIS, UP, tm, 5.0);
1120 return mol.smobbed_copy ();
1124 Beam::forced_stem_count (Grob *me)
1126 Link_array<Item>stems =
1127 Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");
1129 for (int i=0; i < stems.size (); i++)
1133 if (Stem::invisible_b (s))
1136 if (((int)Stem::chord_start_f (s))
1137 && (Stem::get_direction (s) != Stem::get_default_dir (s)))
1147 use filter and standard list functions.
1150 Beam::visible_stem_count (Grob *me)
1152 Link_array<Item>stems =
1153 Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");
1155 for (int i = stems.size (); i--;)
1157 if (!Stem::invisible_b (stems[i]))
1164 Beam::first_visible_stem (Grob *me)
1166 Link_array<Item>stems =
1167 Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");
1169 for (int i = 0; i < stems.size (); i++)
1171 if (!Stem::invisible_b (stems[i]))
1178 Beam::last_visible_stem (Grob *me)
1180 Link_array<Item>stems =
1181 Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");
1182 for (int i = stems.size (); i--;)
1184 if (!Stem::invisible_b (stems[i]))
1193 handle rest under beam (do_post: beams are calculated now)
1194 what about combination of collisions and rest under beam.
1198 rest -> stem -> beam -> interpolate_y_position ()
1200 MAKE_SCHEME_CALLBACK (Beam, rest_collision_callback, 2);
1202 Beam::rest_collision_callback (SCM element_smob, SCM axis)
1204 Grob *rest = unsmob_grob (element_smob);
1205 Axis a = (Axis) gh_scm2int (axis);
1207 assert (a == Y_AXIS);
1209 Grob *st = unsmob_grob (rest->get_grob_property ("stem"));
1212 return gh_double2scm (0.0);
1213 Grob *beam = unsmob_grob (stem->get_grob_property ("beam"));
1215 || !Beam::has_interface (beam)
1216 || !Beam::visible_stem_count (beam))
1217 return gh_double2scm (0.0);
1219 // make callback for rest from this.
1220 // todo: make sure this calced already.
1222 // Interval pos = ly_scm2interval (beam->get_grob_property ("positions"));
1223 Interval pos (0, 0);
1224 SCM s = beam->get_grob_property ("positions");
1225 if (gh_pair_p (s) && gh_number_p (ly_car (s)))
1226 pos = ly_scm2interval (s);
1228 Real dy = pos.delta ();
1229 // ugh -> use commonx
1230 Real x0 = first_visible_stem (beam)->relative_coordinate (0, X_AXIS);
1231 Real dx = last_visible_stem (beam)->relative_coordinate (0, X_AXIS) - x0;
1232 Real dydx = dy && dx ? dy/dx : 0;
1234 Direction d = Stem::get_direction (stem);
1235 Real beamy = (stem->relative_coordinate (0, X_AXIS) - x0) * dydx + pos[LEFT];
1237 Real staff_space = Staff_symbol_referencer::staff_space (rest);
1240 Real rest_dim = rest->extent (rest, Y_AXIS)[d]*2.0 / staff_space; // refp??
1243 = gh_scm2double (rest->get_grob_property ("minimum-beam-collision-distance"));
1245 minimum_dist + -d * (beamy - rest_dim) >? 0;
1247 int stafflines = Staff_symbol_referencer::line_count (rest);
1249 // move discretely by half spaces.
1250 int discrete_dist = int (ceil (dist));
1252 // move by whole spaces inside the staff.
1253 if (discrete_dist < stafflines+1)
1254 discrete_dist = int (ceil (discrete_dist / 2.0)* 2.0);
1256 return gh_double2scm (-d * discrete_dist);
1261 Beam::has_interface (Grob *me)
1263 return me->has_interface (ly_symbol2scm ("beam-interface"));