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);
55 Beam::add_stem (Grob *me, Grob *s)
57 Pointer_group_interface::add_grob (me, ly_symbol2scm ("stems"), s);
59 s->add_dependency (me);
61 assert (!Stem::beam_l (s));
62 s->set_grob_property ("beam", me->self_scm ());
64 add_bound_item (dynamic_cast<Spanner*> (me), dynamic_cast<Item*> (s));
68 Beam::get_multiplicity (Grob *me)
71 for (SCM s = me->get_grob_property ("stems"); gh_pair_p (s); s = ly_cdr (s))
73 Grob *sc = unsmob_grob (ly_car (s));
75 if (Stem::has_interface (sc))
76 m = m >? Stem::beam_count (sc, LEFT) >? Stem::beam_count (sc, RIGHT);
81 /* After pre-processing all directions should be set.
82 Several post-processing routines (stem, slur, script) need stem/beam
84 Currenly, this means that beam has set all stem's directions.
85 [Alternatively, stems could set its own directions, according to
86 their beam, during 'final-pre-processing'.] */
87 MAKE_SCHEME_CALLBACK (Beam, before_line_breaking, 1);
89 Beam::before_line_breaking (SCM smob)
91 Grob *me = unsmob_grob (smob);
93 /* Beams with less than 2 two stems don't make much sense, but could happen
98 For a beam that only has one stem, we try to do some disappearance magic:
99 we revert the flag, and move on to The Eternal Engraving Fields. */
101 int count = visible_stem_count (me);
104 me->warning (_ ("beam has less than two visible stems"));
106 SCM stems = me->get_grob_property ("stems");
107 if (scm_ilength (stems) == 1)
109 me->warning (_ ("Beam has less than two stems. Removing beam."));
111 unsmob_grob (gh_car (stems))->remove_grob_property ("beam");
114 return SCM_UNSPECIFIED;
116 else if (scm_ilength (stems) == 0)
119 return SCM_UNSPECIFIED;
124 if (!Directional_element_interface::get (me))
125 Directional_element_interface::set (me, get_default_dir (me));
127 consider_auto_knees (me);
128 set_stem_directions (me);
129 set_stem_shorten (me);
135 Beam::get_default_dir (Grob *me)
137 Drul_array<int> total;
138 total[UP] = total[DOWN] = 0;
139 Drul_array<int> count;
140 count[UP] = count[DOWN] = 0;
143 Link_array<Item> stems=
144 Pointer_group_interface__extract_grobs (me, (Item*)0, "stems");
146 for (int i=0; i <stems.size (); i++)
149 Direction sd = Directional_element_interface::get (s);
150 int current = sd ? (1 + d * sd)/2
151 : Stem::get_center_distance (s, (Direction)-d);
158 } while (flip (&d) != DOWN);
160 SCM func = me->get_grob_property ("dir-function");
161 SCM s = gh_call2 (func,
162 gh_cons (gh_int2scm (count[UP]),
163 gh_int2scm (count[DOWN])),
164 gh_cons (gh_int2scm (total[UP]),
165 gh_int2scm (total[DOWN])));
167 if (gh_number_p (s) && gh_scm2int (s))
170 /* If dir is not determined: get default */
171 return to_dir (me->get_grob_property ("neutral-direction"));
175 /* Set all stems with non-forced direction to beam direction.
176 Urg: non-forced should become `without/with unforced' direction,
177 once stem gets cleaned-up. */
179 Beam::set_stem_directions (Grob *me)
181 Link_array<Item> stems
182 =Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");
183 Direction d = Directional_element_interface::get (me);
185 for (int i=0; i <stems.size (); i++)
188 SCM force = s->remove_grob_property ("dir-forced");
189 if (!gh_boolean_p (force) || !gh_scm2bool (force))
190 Directional_element_interface::set (s, d);
194 /* Simplistic auto-knees; only consider vertical gap between two
197 `Forced' stem directions are ignored. If you don't want auto-knees,
198 don't set, or unset auto-knee-gap. */
200 Beam::consider_auto_knees (Grob *me)
202 SCM scm = me->get_grob_property ("auto-knee-gap");
204 if (gh_number_p (scm))
208 Real staff_space = Staff_symbol_referencer::staff_space (me);
209 Real gap = gh_scm2double (scm) / staff_space;
211 Direction d = Directional_element_interface::get (me);
212 Link_array<Item> stems=
213 Pointer_group_interface__extract_grobs (me, (Item*)0, "stems");
215 Grob *common = me->common_refpoint (stems[0], Y_AXIS);
216 for (int i=1; i < stems.size (); i++)
217 if (!Stem::invisible_b (stems[i]))
218 common = common->common_refpoint (stems[i], Y_AXIS);
221 for (int i=1; i < stems.size (); i++)
223 if (!Stem::invisible_b (stems[i-1]))
225 if (Stem::invisible_b (stems[l]))
227 if (Stem::invisible_b (stems[i]))
230 Real left = Stem::extremal_heads (stems[l])[d]
231 ->relative_coordinate (common, Y_AXIS);
232 Real right = Stem::extremal_heads (stems[i])[-d]
233 ->relative_coordinate (common, Y_AXIS);
235 Real dy = right - left;
239 knee_y = (right + left) / 2;
247 for (int i=0; i < stems.size (); i++)
249 if (Stem::invisible_b (stems[i]))
252 Real y = Stem::extremal_heads (stems[i])[d]
253 ->relative_coordinate (common, Y_AXIS);
255 Directional_element_interface::set (s, y < knee_y ? UP : DOWN);
256 s->set_grob_property ("dir-forced", SCM_BOOL_T);
262 /* Set stem's shorten property if unset.
264 take some y-position (chord/beam/nearest?) into account
265 scmify forced-fraction */
267 Beam::set_stem_shorten (Grob *m)
269 Spanner*me = dynamic_cast<Spanner*> (m);
271 Real forced_fraction = forced_stem_count (me) / visible_stem_count (me);
273 int multiplicity = get_multiplicity (me);
275 SCM shorten = me->get_grob_property ("beamed-stem-shorten");
276 if (shorten == SCM_EOL)
279 int sz = scm_ilength (shorten);
281 Real staff_space = Staff_symbol_referencer::staff_space (me);
282 SCM shorten_elt = scm_list_ref (shorten,
283 gh_int2scm (multiplicity <? (sz - 1)));
284 Real shorten_f = gh_scm2double (shorten_elt) * staff_space;
286 /* your similar cute comment here */
287 shorten_f *= forced_fraction;
289 me->set_grob_property ("shorten", gh_double2scm (shorten_f));
292 /* Call list of y-dy-callbacks, that handle setting of
293 grob-properties y, dy.
295 User may set grob-properties: y-position-hs and height-hs
296 (to be fixed) that override the calculated y and dy.
298 Because y and dy cannot be calculated and quanted separately, we
299 always calculate both, then check for user override. */
300 MAKE_SCHEME_CALLBACK (Beam, after_line_breaking, 1);
302 Beam::after_line_breaking (SCM smob)
304 Grob *me = unsmob_grob (smob);
306 /* Copy to mutable list. */
307 SCM s = ly_deep_copy (me->get_grob_property ("positions"));
308 me->set_grob_property ("positions", s);
310 if (ly_car (s) != SCM_BOOL_F)
311 return SCM_UNSPECIFIED;
313 SCM callbacks = me->get_grob_property ("position-callbacks");
314 for (SCM i = callbacks; gh_pair_p (i); i = ly_cdr (i))
315 gh_call1 (ly_car (i), smob);
317 return SCM_UNSPECIFIED;
328 MAKE_SCHEME_CALLBACK (Beam, new_quanting, 1);
330 Beam::new_quanting (SCM smob)
332 Grob *me = unsmob_grob (smob);
334 SCM s = me->get_grob_property ("positions");
335 Real yl = gh_scm2double (gh_car(s));
336 Real yr = gh_scm2double (gh_cdr(s));
338 Real ss = Staff_symbol_referencer::staff_space (me);
339 Real thickness = gh_scm2double (me->get_grob_property ("thickness")) / ss;
340 Real slt = me->paper_l()->get_var ("stafflinethickness") / ss;
343 Real sit = (thickness - slt) / 2;
345 Real hang = 1.0 - (thickness - slt) / 2;
346 Real quants [] = {straddle, sit, inter, hang };
348 int num_quants = int(sizeof(quants)/sizeof (Real));
352 const int REGION_SIZE = 3;
353 // -> result indexes between 70 and 575
354 for (int i = -REGION_SIZE ; i < REGION_SIZE; i++)
355 for (int j = 0; j < num_quants; j++)
357 quantsl.push (i + quants[j] + int (yl));
358 quantsr.push (i + quants[j] + int (yr));
361 Array<Quant_score> qscores;
363 for(int l =0; l < quantsl.size(); l++)
364 for(int r =0; r < quantsr.size(); r++)
375 SCM score_funcs = me->get_grob_property("quant-score-functions");
376 for (SCM s = score_funcs; gh_pair_p (s); s = gh_cdr (s))
379 for (int i = qscores.size(); i--;)
382 // if (qscores[i].demerits < 1000)
383 if (qscores[i].demerits < 100)
385 SCM score = gh_call3 (f,
387 gh_double2scm (qscores[i].yl),
388 gh_double2scm (qscores[i].yr));
390 qscores[i].demerits += gh_scm2double (score);
397 for (int i = qscores.size(); i--;)
399 if (qscores[i].demerits < best)
401 best = qscores [i].demerits ;
407 me->set_grob_property ("positions",
408 gh_cons (gh_double2scm (qscores[best_idx].yl),
409 gh_double2scm (qscores[best_idx].yr))
415 me->set_grob_property ("quant-score",
416 gh_double2scm (qscores[best_idx].demerits));
417 me->set_grob_property ("best-idx", gh_int2scm (best_idx));
420 return SCM_UNSPECIFIED;
423 MAKE_SCHEME_CALLBACK (Beam, score_slopes_dy, 3);
425 Beam::score_slopes_dy (SCM smob, SCM syl, SCM syr)
427 Grob*me = unsmob_grob(smob);
428 Real yl = gh_scm2double (syl);
429 Real yr = gh_scm2double (syr);
432 SCM sdy = me->get_grob_property("least-squares-dy");
433 SCM posns = me->get_grob_property ("positions");
435 Real dy_mus = gh_number_p (sdy) ? gh_scm2double (sdy) : 0.0;
436 Real dy_damp = - gh_scm2double (gh_car(posns)) + gh_scm2double (gh_cdr (posns));
439 if (sign (dy_damp) != sign (dy))
444 dem += 400* (0 >? (fabs(dy) - fabs(dy_mus)));
447 dem += shrink_extra_weight (fabs (dy_damp) - fabs(dy))* 10;
448 return gh_double2scm (dem);
451 MAKE_SCHEME_CALLBACK (Beam, score_stem_lengths, 3);
453 Beam::score_stem_lengths (SCM smob, SCM syl, SCM syr)
455 Grob*me = unsmob_grob(smob);
456 Real yl = gh_scm2double (syl);
457 Real yr = gh_scm2double (syr);
459 Link_array<Item> stems=
460 Pointer_group_interface__extract_grobs (me, (Item*)0, "stems");
462 Real demerit_score = 0.0 ;
464 for (int i=0; i < stems.size (); i++)
467 if (Stem::invisible_b (s))
470 Real current_y = calc_stem_y_f (me, s, Interval(yl,yr));
471 Stem_info info = Stem::calc_stem_info (s);
472 Direction d = Directional_element_interface::get (s);
474 demerit_score += 500 * ( 0 >? (info.miny_f_ - d*current_y));
475 demerit_score += 500 * ( 0 >? (d * current_y - info.maxy_f_));
477 demerit_score += 5 * shrink_extra_weight (d * current_y - info.idealy_f_);
480 demerit_score *= 2.0 /stems.size();
482 return gh_double2scm (demerit_score);
493 MAKE_SCHEME_CALLBACK (Beam, score_forbidden_quants, 3);
495 Beam::score_forbidden_quants (SCM smob, SCM syl, SCM syr)
497 Grob*me = unsmob_grob(smob);
498 Real yl = gh_scm2double (syl);
499 Real yr = gh_scm2double (syr);
501 Real rad = Staff_symbol_referencer::staff_radius (me);
503 if (fabs (yl) < rad && fabs( my_modf(yl) - 0.5) < 1e-3)
505 if (fabs (yr) < rad && fabs( my_modf(yr) - 0.5) < 1e-3)
509 int multiplicity = get_multiplicity (me);
510 // todo: use multiplicity of outer stems.
511 if (multiplicity >= 2)
513 Real slt = me->paper_l()->get_var("stafflinethickness");
514 Real ss = Staff_symbol_referencer::staff_space(me);
515 Real thickness = gh_scm2double (me->get_grob_property ("thickness"))
518 Real beam_space= (2*ss + slt - 3 *thickness) / 2.0;
519 if (multiplicity >= 4)
521 beam_space = (3*ss + slt - 4 * thickness) /3.0;
525 Real sit = (thickness - slt) / 2;
527 Real hang = 1.0 - (thickness - slt) / 2;
529 Direction dir = Directional_element_interface::get (me);
530 if (fabs (yl - dir * (beam_space + thickness)) < rad
531 && fabs (my_modf (yl) - inter) < 1e-3)
533 if (fabs (yr - dir * (beam_space + thickness)) < rad
534 && fabs (my_modf (yr) - inter) < 1e-3)
537 // hmm, without Interval/Drul_array, you get ~ 4x same code...
538 if (fabs (yl - dir * (beam_space + thickness)) < rad + inter)
540 if (dir == UP && dy <= 1e-3
541 && fabs (my_modf (yl) - sit) < 1e-3)
544 if (dir == DOWN && dy >= 1e-3
545 && fabs (my_modf (yl) - hang) < 1e-3)
549 if (fabs (yr - dir * (beam_space + thickness)) < rad + inter)
551 if (dir == UP && dy >= 1e-3
552 && fabs (my_modf (yr) - sit) < 1e-3)
555 if (dir == DOWN && dy <= 1e-3
556 && fabs (my_modf (yr) - hang) < 1e-3)
560 if (multiplicity >= 3)
562 if (fabs (yl - 2 * dir * (beam_space + thickness)) < rad + inter)
564 if (dir == UP && dy <= 1e-3
565 && fabs (my_modf (yl) - straddle) < 1e-3)
568 if (dir == DOWN && dy >= 1e-3
569 && fabs (my_modf (yl) - straddle) < 1e-3)
573 if (fabs (yr - 2 * dir * (beam_space + thickness)) < rad + inter)
575 if (dir == UP && dy >= 1e-3
576 && fabs (my_modf (yr) - straddle) < 1e-3)
579 if (dir == DOWN && dy <= 1e-3
580 && fabs (my_modf (yr) - straddle) < 1e-3)
586 return gh_double2scm ( dem);
591 MAKE_SCHEME_CALLBACK (Beam, least_squares, 1);
593 Beam::least_squares (SCM smob)
595 Grob *me = unsmob_grob (smob);
597 int count = visible_stem_count (me);
602 me->set_grob_property ("positions", ly_interval2scm (pos));
603 return SCM_UNSPECIFIED;
606 Direction dir = Directional_element_interface::get (me);
608 Interval ideal (Stem::calc_stem_info (first_visible_stem (me)).idealy_f_,
609 Stem::calc_stem_info (last_visible_stem (me)).idealy_f_);
613 Interval chord (Stem::chord_start_f (first_visible_stem (me)),
614 Stem::chord_start_f (last_visible_stem (me)));
618 TODO : use scoring for this.
620 complicated, because we take stem-info.ideal for determining
624 /* Make simple beam on middle line have small tilt */
625 if (!ideal[LEFT] && chord.delta () && count == 2)
627 Direction d = (Direction)(sign (chord.delta ()) * dir);
628 pos[d] = gh_scm2double (me->get_grob_property ("thickness")) / 2
641 Array<Offset> ideals;
643 // ugh -> use commonx
644 Real x0 = first_visible_stem (me)->relative_coordinate (0, X_AXIS);
645 Link_array<Item> stems=
646 Pointer_group_interface__extract_grobs (me, (Item*)0, "stems");
648 for (int i=0; i < stems.size (); i++)
651 if (Stem::invisible_b (s))
653 ideals.push (Offset (s->relative_coordinate (0, X_AXIS) - x0,
654 Stem::calc_stem_info (s).idealy_f_));
658 minimise_least_squares (&dydx, &y, ideals);
660 Real dx = last_visible_stem (me)->relative_coordinate (0, X_AXIS) - x0;
662 me->set_grob_property ("least-squares-dy", gh_double2scm (dy * dir));
664 pos = Interval (y*dir, (y+dy) * dir);
667 me->set_grob_property ("positions", ly_interval2scm (pos));
668 return SCM_UNSPECIFIED;
671 MAKE_SCHEME_CALLBACK (Beam, check_concave, 1);
673 Beam::check_concave (SCM smob)
675 Grob *me = unsmob_grob (smob);
677 Link_array<Item> stems =
678 Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");
680 for (int i = 0; i < stems.size ();)
682 if (Stem::invisible_b (stems[i]))
688 if (stems.size () < 3)
689 return SCM_UNSPECIFIED;
691 /* Concaveness try #2: Sum distances of inner noteheads that
692 fall outside the interval of the two outer noteheads */
694 Interval iv (Stem::chord_start_f (stems[0]),
695 Stem::chord_start_f (stems.top ()));
697 if (iv[MAX] < iv[MIN])
700 for (int i = 1; i < stems.size () - 1; i++)
703 Real f = Stem::chord_start_f (stems[i]);
704 if ((c = f - iv[MAX]) > 0)
706 else if ((c = f - iv[MIN]) < 0)
710 Direction dir = Directional_element_interface::get (me);
713 Real concaveness = concave / (stems.size () - 2);
714 /* ugh: this is the a kludge to get input/regression/beam-concave.ly
715 to behave as baerenreiter. */
716 concaveness /= (stems.size () - 2);
718 Real r = gh_scm2double (me->get_grob_property ("concaveness-threshold"));
720 /* TODO: some sort of damping iso -> plain horizontal */
723 Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
724 Real r = pos.linear_combination (0);
725 me->set_grob_property ("positions", ly_interval2scm (Interval (r, r)));
726 me->set_grob_property ("least-squares-dy", gh_double2scm (0));
729 return SCM_UNSPECIFIED;
732 /* This neat trick is by Werner Lemberg,
733 damped = tanh (slope)
734 corresponds with some tables in [Wanske] CHECKME */
735 MAKE_SCHEME_CALLBACK (Beam, slope_damping, 1);
737 Beam::slope_damping (SCM smob)
739 Grob *me = unsmob_grob (smob);
741 if (visible_stem_count (me) <= 1)
742 return SCM_UNSPECIFIED;
744 SCM s = me->get_grob_property ("damping");
745 int damping = gh_scm2int (s);
749 Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
750 Real dy = pos.delta ();
752 // ugh -> use commonx
753 Real dx = last_visible_stem (me)->relative_coordinate (0, X_AXIS)
754 - first_visible_stem (me)->relative_coordinate (0, X_AXIS);
755 Real dydx = dy && dx ? dy/dx : 0;
756 dydx = 0.6 * tanh (dydx) / damping;
758 Real damped_dy = dydx * dx;
759 pos[LEFT] += (dy - damped_dy) / 2;
760 pos[RIGHT] -= (dy - damped_dy) / 2;
762 me->set_grob_property ("positions", ly_interval2scm (pos));
764 return SCM_UNSPECIFIED;
767 /* Prevent interference from stafflines. */
769 Beam::quantise_interval (Grob *me, Interval pos, Direction quant_dir)
771 int multiplicity = get_multiplicity (me);
773 Real staff_space = Staff_symbol_referencer::staff_space (me);
774 Real thick = me->paper_l ()->get_var ("stafflinethickness");
775 Direction dir = Directional_element_interface::get (me);
776 Real dy = pos.delta ();
778 Drul_array<Interval> bounds;
783 ? me->get_grob_property ("left-position-quant-function")
784 : me->get_grob_property ("right-position-quant-function");
786 SCM quants = scm_apply (proc,
788 scm_list_n (gh_int2scm (multiplicity),
791 gh_double2scm (thick / staff_space),
797 for (SCM i = quants; gh_pair_p (i); i = ly_cdr (i))
798 a.push (gh_scm2double (ly_car (i)));
803 bounds[d] = quantise_iv (a, pos[d]*dir/staff_space) * staff_space;
805 while (flip (&d) != LEFT);
809 // quant direction hints disabled for now
810 int q = 0;//(int)quant_dir;
812 /* TODO: make smart choice, find best left/right quants pair.
814 Slope should never be steeper than least_squares (before damping)
816 Slope should never be reduced to zero.
818 SCM s = me->get_grob_property ("least-squares-dy");
819 Real lsdy = gh_number_p (s) ? gh_scm2double (s) : 0;
821 // Interval qpos (0, 1000 * sign (dy));
823 Real epsilon = staff_space / 10;
824 Direction ldir = LEFT;
827 Direction rdir = LEFT;
830 Interval i (bounds[LEFT][ldir]*dir, bounds[RIGHT][rdir]*dir);
832 || (abs (i.delta ()) <= abs (lsdy) + epsilon
833 && sign (i.delta ()) == sign (lsdy)))
834 && (abs (abs (i.delta ()) - ady)
835 <= abs (abs (qpos.delta ()) - ady))
836 && sign (i.delta ()) == sign (pos.delta ())
838 || (i[LEFT]*q >= pos[LEFT]*q && i[RIGHT]*q
842 while (flip (&rdir) != LEFT);
844 while (flip (&ldir) != LEFT);
850 /* Quantise vertical position (left and right) of beam.
851 Generalisation of [Ross]. */
852 MAKE_SCHEME_CALLBACK (Beam, quantise_position, 1);
854 Beam::quantise_position (SCM smob)
856 Grob *me = unsmob_grob (smob);
858 Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
859 Real y_shift = check_stem_length_f (me, pos);
861 Real staff_space = Staff_symbol_referencer::staff_space (me);
863 Direction dir = Directional_element_interface::get (me);
864 for (int i = 0; i < 10; i++)
866 Interval qpos = quantise_interval (me, pos, CENTER);
867 // how to check for uninitised interval, (inf, -inf)?
868 if (qpos[LEFT] < 1000)
870 y_shift = check_stem_length_f (me, qpos);
871 if (y_shift * dir < staff_space / 2)
877 pos += ((i + 1) * ((i % 2) * -2 + 1)) * dir * staff_space / 4;
881 me->set_grob_property ("positions", ly_interval2scm (pos));
882 set_stem_lengths (me);
885 pos = ly_scm2interval (me->get_grob_property ("positions"));
887 y_shift = check_stem_length_f (me, pos);
889 Real half_space = Staff_symbol_referencer::staff_space (me) / 2;
891 if (y_shift > half_space / 4)
895 /* for significantly lengthened or shortened stems,
896 request quanting the other way.
898 if (abs (y_shift) > half_space / 2)
899 quant_dir = sign (y_shift) * Directional_element_interface::get (me);
900 pos = quantise_interval (me, pos, (Direction)quant_dir);
903 me->set_grob_property ("positions", ly_interval2scm (pos));
906 return SCM_UNSPECIFIED;
909 MAKE_SCHEME_CALLBACK (Beam, end_after_line_breaking, 1);
911 Beam::end_after_line_breaking (SCM smob)
913 Grob *me = unsmob_grob (smob);
914 set_stem_lengths (me);
916 return SCM_UNSPECIFIED;
920 Calculate the Y position of the stem-end, given the Y-left, Y-right
921 in POS, and for stem S.
924 Beam::calc_stem_y_f (Grob *me, Item* s, Interval pos)
926 int beam_multiplicity = get_multiplicity (me);
927 int stem_multiplicity = (Stem::flag_i (s) - 2) >? 0;
929 SCM space_proc = me->get_grob_property ("space-function");
930 SCM space = gh_call1 (space_proc, gh_int2scm (beam_multiplicity));
932 Real thick = gh_scm2double (me->get_grob_property ("thickness"));
933 Real interbeam_f = gh_scm2double (space);
935 // ugh -> use commonx
936 Real x0 = first_visible_stem (me)->relative_coordinate (0, X_AXIS);
937 Real dx = last_visible_stem (me)->relative_coordinate (0, X_AXIS) - x0;
938 Real dy = pos.delta ();
939 Real stem_y = (dy && dx
940 ? (s->relative_coordinate (0, X_AXIS) - x0) / dx
945 Direction dir = Directional_element_interface::get (me);
946 Direction sdir = Directional_element_interface::get (s);
951 stem_y -= dir * (thick / 2 + (beam_multiplicity - 1) * interbeam_f);
953 // huh, why not for first visible?
954 if (Staff_symbol_referencer::staff_symbol_l (s)
955 != Staff_symbol_referencer::staff_symbol_l (last_visible_stem (me)))
956 stem_y += Directional_element_interface::get (me)
957 * (beam_multiplicity - stem_multiplicity) * interbeam_f;
963 /* Make very sure that we don't have stems that are too short.
964 Try our best not to have stems that are too long (think: knees).
966 Optionally (testing): try to lengthen more, to reach more ideal
969 Beam::check_stem_length_f (Grob *me, Interval pos)
973 Direction dir = Directional_element_interface::get (me);
975 Link_array<Item> stems=
976 Pointer_group_interface__extract_grobs (me, (Item*)0, "stems");
979 int ideal_lengthen_count = 0;
980 Real ideal_lengthen = 0;
981 int ideal_shorten_count = 0;
982 Real ideal_shorten = 0;
984 for (int i=0; i < stems.size (); i++)
987 if (Stem::invisible_b (s))
990 knee |= dir != Directional_element_interface::get (s);
992 Real stem_y = calc_stem_y_f (me, s, pos);
995 Stem_info info = Stem::calc_stem_info (s);
997 shorten = shorten <? info.maxy_f_ - stem_y;
998 lengthen = lengthen >? info.miny_f_ - stem_y;
1000 if (info.idealy_f_ - stem_y > 0)
1003 ideal_lengthen += info.idealy_f_ - stem_y;
1004 ideal_lengthen_count++;
1006 ideal_lengthen = ideal_lengthen >? info.idealy_f_ - stem_y;
1007 ideal_lengthen_count = 1;
1010 else if (info.idealy_f_ - stem_y < 0)
1013 ideal_shorten += info.idealy_f_ - stem_y;
1014 ideal_shorten_count++;
1016 ideal_shorten = ideal_shorten <? info.idealy_f_ - stem_y;
1017 ideal_shorten_count = 1;
1022 if (lengthen && shorten)
1023 me->warning (_ ("weird beam vertical offset"));
1025 if (ideal_lengthen_count)
1026 lengthen = (ideal_lengthen / ideal_lengthen_count) >? lengthen;
1027 if (knee && ideal_shorten_count)
1028 shorten = (ideal_shorten / ideal_shorten_count) <? shorten;
1030 if (lengthen && shorten)
1031 return dir * (lengthen + shorten);
1033 return dir * (shorten ? shorten : lengthen);
1037 Hmm. At this time, beam position and slope are determined. Maybe,
1038 stem directions and length should set to relative to the chord's
1039 position of the beam. */
1041 Beam::set_stem_lengths (Grob *me)
1043 Link_array<Item> stems=
1044 Pointer_group_interface__extract_grobs (me, (Item*)0, "stems");
1046 if (stems.size () <= 1)
1049 Grob *common = me->common_refpoint (stems[0], Y_AXIS);
1050 for (int i=1; i < stems.size (); i++)
1051 if (!Stem::invisible_b (stems[i]))
1052 common = common->common_refpoint (stems[i], Y_AXIS);
1054 Direction dir = Directional_element_interface::get (me);
1055 Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
1056 Real staff_space = Staff_symbol_referencer::staff_space (me);
1057 Real thick = gh_scm2double (me->get_grob_property ("thickness"));
1058 bool ps_testing = to_boolean (ly_symbol2scm ("ps-testing"));
1059 for (int i=0; i < stems.size (); i++)
1062 if (Stem::invisible_b (s))
1065 Real stem_y = calc_stem_y_f (me, s, pos);
1067 // doesn't play well with dvips
1069 if (Stem::get_direction (s) == dir)
1070 stem_y += Stem::get_direction (s) * thick / 2;
1072 /* caution: stem measures in staff-positions */
1073 Real id = me->relative_coordinate (common, Y_AXIS)
1074 - stems[i]->relative_coordinate (common, Y_AXIS);
1075 Stem::set_stemend (s, (stem_y + id) / staff_space * 2);
1080 Beam::set_beaming (Grob *me, Beaming_info_list *beaming)
1082 Link_array<Grob> stems=
1083 Pointer_group_interface__extract_grobs (me, (Grob *)0, "stems");
1086 for (int i=0; i < stems.size (); i++)
1090 /* Don't overwrite user override (?) */
1091 if (Stem::beam_count (stems[i], d) == -1
1092 /* Don't set beaming for outside of outer stems */
1093 && ! (d == LEFT && i == 0)
1094 && ! (d == RIGHT && i == stems.size () -1))
1096 int b = beaming->infos_.elem (i).beams_i_drul_[d];
1097 Stem::set_beaming (stems[i], b, d);
1100 while (flip (&d) != LEFT);
1107 beams to go with one stem.
1112 Beam::stem_beams (Grob *me, Item *here, Item *next, Item *prev, Real dydx)
1114 // ugh -> use commonx
1116 && !(next->relative_coordinate (0, X_AXIS)
1117 > here->relative_coordinate (0, X_AXIS)))
1119 && !(prev->relative_coordinate (0, X_AXIS)
1120 < here->relative_coordinate (0, X_AXIS))))
1121 programming_error ("Beams are not left-to-right");
1123 int multiplicity = get_multiplicity (me);
1125 SCM space_proc = me->get_grob_property ("space-function");
1126 SCM space = gh_call1 (space_proc, gh_int2scm (multiplicity));
1128 Real thick = gh_scm2double (me->get_grob_property ("thickness"));
1129 Real interbeam_f = gh_scm2double (space);
1131 Real bdy = interbeam_f;
1134 Molecule rightbeams;
1137 if (!Stem::first_head (here))
1140 int t = Stem::type_i (here);
1142 SCM proc = me->get_grob_property ("flag-width-function");
1143 SCM result = gh_call1 (proc, gh_int2scm (t));
1144 nw_f = gh_scm2double (result);
1148 Direction dir = Directional_element_interface::get (me);
1150 /* [Tremolo] beams on whole notes may not have direction set? */
1152 dir = Directional_element_interface::get (here);
1155 /* half beams extending to the left. */
1158 int lhalfs= lhalfs = Stem::beam_count (here, LEFT)
1159 - Stem::beam_count (prev, RIGHT);
1160 int lwholebeams= Stem::beam_count (here, LEFT)
1161 <? Stem::beam_count (prev, RIGHT);
1163 /* Half beam should be one note-width,
1164 but let's make sure two half-beams never touch */
1166 // FIXME: TODO (check) stem width / sloped beams
1167 Real w = here->relative_coordinate (0, X_AXIS)
1168 - prev->relative_coordinate (0, X_AXIS);
1169 Real stem_w = gh_scm2double (prev->get_grob_property ("thickness"))
1171 * me->paper_l ()->get_var ("stafflinethickness");
1175 if (lhalfs) // generates warnings if not
1176 a = Lookup::beam (dydx, w + stem_w, thick);
1177 a.translate (Offset (-w, -w * dydx));
1178 a.translate_axis (-stem_w/2, X_AXIS);
1179 for (int j = 0; j < lhalfs; j++)
1182 b.translate_axis (-dir * bdy * (lwholebeams+j), Y_AXIS);
1183 leftbeams.add_molecule (b);
1189 int rhalfs = Stem::beam_count (here, RIGHT)
1190 - Stem::beam_count (next, LEFT);
1191 int rwholebeams= Stem::beam_count (here, RIGHT)
1192 <? Stem::beam_count (next, LEFT);
1194 Real w = next->relative_coordinate (0, X_AXIS)
1195 - here->relative_coordinate (0, X_AXIS);
1197 Real stem_w = gh_scm2double (next->get_grob_property ("thickness"))
1199 * me->paper_l ()->get_var ("stafflinethickness");
1201 Molecule a = Lookup::beam (dydx, w + stem_w, thick);
1202 a.translate_axis (- stem_w/2, X_AXIS);
1206 SCM gap = me->get_grob_property ("gap");
1207 if (gh_number_p (gap))
1209 int gap_i = gh_scm2int ((gap));
1210 int nogap = rwholebeams - gap_i;
1212 for (; j < nogap; j++)
1215 b.translate_axis (-dir * bdy * j, Y_AXIS);
1216 rightbeams.add_molecule (b);
1218 if (Stem::invisible_b (here))
1223 a = Lookup::beam (dydx, w + stem_w, thick);
1226 for (; j < rwholebeams; j++)
1230 if (Stem::invisible_b (here))
1231 // ugh, see chord-tremolo.ly
1232 tx = (-dir + 1) / 2 * nw_f * 1.5 + gap_f/4;
1235 b.translate (Offset (tx, -dir * bdy * j));
1236 rightbeams.add_molecule (b);
1241 a = Lookup::beam (dydx, w, thick);
1243 for (; j < rwholebeams + rhalfs; j++)
1246 b.translate_axis (- dir * bdy * j, Y_AXIS);
1247 rightbeams.add_molecule (b);
1251 leftbeams.add_molecule (rightbeams);
1253 /* Does beam quanting think of the asymetry of beams?
1254 Refpoint is on bottom of symbol. (FIXTHAT) --hwn. */
1259 MAKE_SCHEME_CALLBACK (Beam, brew_molecule, 1);
1261 Beam::brew_molecule (SCM smob)
1263 Grob *me =unsmob_grob (smob);
1266 if (!gh_pair_p (me->get_grob_property ("stems")))
1269 Link_array<Item>stems =
1270 Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");
1271 if (visible_stem_count (me))
1273 // ugh -> use commonx
1274 x0 = first_visible_stem (me)->relative_coordinate (0, X_AXIS);
1275 dx = last_visible_stem (me)->relative_coordinate (0, X_AXIS) - x0;
1279 x0 = stems[0]->relative_coordinate (0, X_AXIS);
1280 dx = stems.top ()->relative_coordinate (0, X_AXIS) - x0;
1283 Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
1284 Real dy = pos.delta ();
1285 Real dydx = dy && dx ? dy/dx : 0;
1287 for (int i=0; i < stems.size (); i++)
1289 Item *item = stems[i];
1290 Item *prev = (i > 0)? stems[i-1] : 0;
1291 Item *next = (i < stems.size ()-1) ? stems[i+1] :0;
1293 Molecule sb = stem_beams (me, item, next, prev, dydx);
1294 Real x = item->relative_coordinate (0, X_AXIS) - x0;
1295 sb.translate (Offset (x, x * dydx + pos[LEFT]));
1296 mol.add_molecule (sb);
1299 mol.translate_axis (x0
1300 - dynamic_cast<Spanner*> (me)
1301 ->get_bound (LEFT)->relative_coordinate (0, X_AXIS),
1307 This code prints the demerits for each beam. Perhaps this
1308 should be switchable for those who want to twiddle with the
1314 str += to_str (gh_scm2int (me->get_grob_property ("best-idx")));
1317 str += to_str (gh_scm2double (me->get_grob_property ("quant-score")),
1320 SCM properties = Font_interface::font_alist_chain (me);
1322 Molecule tm = Text_item::text2molecule (me, gh_str02scm (str.ch_C()), properties);
1323 mol.add_at_edge (Y_AXIS, UP, tm, 5.0);
1326 return mol.smobbed_copy ();
1330 Beam::forced_stem_count (Grob *me)
1332 Link_array<Item>stems =
1333 Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");
1335 for (int i=0; i < stems.size (); i++)
1339 if (Stem::invisible_b (s))
1342 if (((int)Stem::chord_start_f (s))
1343 && (Stem::get_direction (s) != Stem::get_default_dir (s)))
1353 use filter and standard list functions.
1356 Beam::visible_stem_count (Grob *me)
1358 Link_array<Item>stems =
1359 Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");
1361 for (int i = stems.size (); i--;)
1363 if (!Stem::invisible_b (stems[i]))
1370 Beam::first_visible_stem (Grob *me)
1372 Link_array<Item>stems =
1373 Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");
1375 for (int i = 0; i < stems.size (); i++)
1377 if (!Stem::invisible_b (stems[i]))
1384 Beam::last_visible_stem (Grob *me)
1386 Link_array<Item>stems =
1387 Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");
1388 for (int i = stems.size (); i--;)
1390 if (!Stem::invisible_b (stems[i]))
1399 handle rest under beam (do_post: beams are calculated now)
1400 what about combination of collisions and rest under beam.
1404 rest -> stem -> beam -> interpolate_y_position ()
1406 MAKE_SCHEME_CALLBACK (Beam, rest_collision_callback, 2);
1408 Beam::rest_collision_callback (SCM element_smob, SCM axis)
1410 Grob *rest = unsmob_grob (element_smob);
1411 Axis a = (Axis) gh_scm2int (axis);
1413 assert (a == Y_AXIS);
1415 Grob *st = unsmob_grob (rest->get_grob_property ("stem"));
1418 return gh_double2scm (0.0);
1419 Grob *beam = unsmob_grob (stem->get_grob_property ("beam"));
1421 || !Beam::has_interface (beam)
1422 || !Beam::visible_stem_count (beam))
1423 return gh_double2scm (0.0);
1425 // make callback for rest from this.
1426 // todo: make sure this calced already.
1428 // Interval pos = ly_scm2interval (beam->get_grob_property ("positions"));
1429 Interval pos (0, 0);
1430 SCM s = beam->get_grob_property ("positions");
1431 if (gh_pair_p (s) && gh_number_p (ly_car (s)))
1432 pos = ly_scm2interval (s);
1434 Real dy = pos.delta ();
1435 // ugh -> use commonx
1436 Real x0 = first_visible_stem (beam)->relative_coordinate (0, X_AXIS);
1437 Real dx = last_visible_stem (beam)->relative_coordinate (0, X_AXIS) - x0;
1438 Real dydx = dy && dx ? dy/dx : 0;
1440 Direction d = Stem::get_direction (stem);
1441 Real beamy = (stem->relative_coordinate (0, X_AXIS) - x0) * dydx + pos[LEFT];
1443 Real staff_space = Staff_symbol_referencer::staff_space (rest);
1446 Real rest_dim = rest->extent (rest, Y_AXIS)[d]*2.0 / staff_space; // refp??
1449 = gh_scm2double (rest->get_grob_property ("minimum-beam-collision-distance"));
1451 minimum_dist + -d * (beamy - rest_dim) >? 0;
1453 int stafflines = Staff_symbol_referencer::line_count (rest);
1455 // move discretely by half spaces.
1456 int discrete_dist = int (ceil (dist));
1458 // move by whole spaces inside the staff.
1459 if (discrete_dist < stafflines+1)
1460 discrete_dist = int (ceil (discrete_dist / 2.0)* 2.0);
1462 return gh_double2scm (-d * discrete_dist);
1467 Beam::has_interface (Grob *me)
1469 return me->has_interface (ly_symbol2scm ("beam-interface"));