2 beam.cc -- implement Beam
4 source file of the GNU LilyPond music typesetter
6 (c) 1997--2004 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7 Jan Nieuwenhuizen <janneke@gnu.org>
13 - Determine auto knees based on positions if it's set by the user.
15 - the code is littered with * and / staff_space calls for
16 #'positions. Consider moving to real-world coordinates?
18 Problematic issue is user tweaks (user tweaks are in staff-coordinates.)
22 - Stems run to the Y-center of the beam.
24 - beam_translation is the offset between Y centers of the beam.
30 #include <math.h> // tanh.
33 #include "interval-set.hh"
34 #include "directional-element-interface.hh"
37 #include "least-squares.hh"
39 #include "output-def.hh"
41 #include "group-interface.hh"
42 #include "staff-symbol-referencer.hh"
49 #include "text-item.hh" // debug output.
50 #include "font-interface.hh" // debug output.
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::get_beam (s));
62 s->set_property ("beam", me->self_scm ());
64 add_bound_item (dynamic_cast<Spanner*> (me), dynamic_cast<Item*> (s));
69 Beam::get_thickness (Grob * me)
71 return robust_scm2double (me->get_property ("thickness"), 0)
72 * Staff_symbol_referencer::staff_space (me);
75 /* Return the translation between 2 adjoining beams. */
77 Beam::get_beam_translation (Grob *me)
79 SCM func = me->get_property ("space-function");
81 if (ly_c_procedure_p (func))
83 SCM s = scm_call_2 (func, me->self_scm (), scm_int2num (get_beam_count (me)));
84 return scm_to_double (s);
92 /* Maximum beam_count. */
94 Beam::get_beam_count (Grob *me)
97 for (SCM s = me->get_property ("stems"); scm_is_pair (s); s = scm_cdr (s))
99 Grob *stem = unsmob_grob (scm_car (s));
100 m = m >? (Stem::beam_multiplicity (stem).length () + 1);
107 Space return space between beams.
109 MAKE_SCHEME_CALLBACK (Beam, space_function, 2);
111 Beam::space_function (SCM smob, SCM beam_count)
113 Grob *me = unsmob_grob (smob);
115 Real staff_space = Staff_symbol_referencer::staff_space (me);
116 Real line = Staff_symbol_referencer::line_thickness (me);
117 Real thickness = get_thickness (me);
119 Real beam_translation = scm_to_int (beam_count) < 4
120 ? (2*staff_space + line - thickness) / 2.0
121 : (3*staff_space + line - thickness) / 3.0;
123 return scm_make_real (beam_translation);
127 /* After pre-processing all directions should be set.
128 Several post-processing routines (stem, slur, script) need stem/beam
130 Currenly, this means that beam has set all stem's directions.
131 [Alternatively, stems could set its own directions, according to
132 their beam, during 'final-pre-processing'.] */
133 MAKE_SCHEME_CALLBACK (Beam, before_line_breaking, 1);
135 Beam::before_line_breaking (SCM smob)
137 Grob *me = unsmob_grob (smob);
139 /* Beams with less than 2 two stems don't make much sense, but could happen
144 For a beam that only has one stem, we try to do some disappearance magic:
145 we revert the flag, and move on to The Eternal Engraving Fields. */
147 int count = visible_stem_count (me);
150 me->warning (_ ("beam has less than two visible stems"));
152 SCM stems = me->get_property ("stems");
153 if (scm_ilength (stems) == 1)
155 me->warning (_ ("removing beam with less than two stems"));
157 unsmob_grob (scm_car (stems))->set_property ("beam", SCM_EOL);
160 return SCM_UNSPECIFIED;
162 else if (scm_ilength (stems) == 0)
165 return SCM_UNSPECIFIED;
170 Direction d = get_default_dir (me);
172 consider_auto_knees (me);
173 set_stem_directions (me, d);
177 set_stem_shorten (me);
185 We want a maximal number of shared beams, but if there is choice, we
186 take the one that is closest to the end of the stem. This is for situations like
199 position_with_maximal_common_beams (SCM left_beaming, SCM right_beaming,
203 Slice lslice = int_list_to_slice (scm_cdr (left_beaming));
207 for (int i = lslice[-left_dir];
208 (i - lslice[left_dir])* left_dir <= 0 ; i+= left_dir)
211 for ( SCM s = scm_car (right_beaming); scm_is_pair (s); s = scm_cdr (s))
213 int k = - right_dir * scm_to_int (scm_car (s)) + i;
214 if (scm_c_memq (scm_int2num (k), left_beaming) != SCM_BOOL_F)
218 if (count >= best_count)
229 Beam::connect_beams (Grob *me)
231 Link_array<Grob> stems =
232 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
235 last_int.set_empty ();
236 SCM last_beaming = SCM_EOL;
237 Direction last_dir = CENTER;
238 for (int i = 0; i< stems.size (); i++)
240 Grob *this_stem = stems[i];
241 SCM this_beaming = this_stem->get_property ("beaming");
243 Direction this_dir = get_grob_direction (this_stem);
244 if (scm_is_pair (last_beaming) && scm_is_pair (this_beaming))
246 int start_point = position_with_maximal_common_beams
247 (last_beaming, this_beaming,
254 if (d == RIGHT && i == stems.size ()-1)
257 new_slice.set_empty ();
258 SCM s = index_get_cell (this_beaming, d);
259 for (; scm_is_pair (s); s = scm_cdr (s))
262 start_point - this_dir * scm_to_int (scm_car (s));
264 new_slice.add_point (new_beam_pos);
265 scm_set_car_x (s, scm_int2num (new_beam_pos));
270 while (flip (&d) != LEFT);
272 if (!new_slice.is_empty ())
273 last_int = new_slice;
277 scm_set_car_x ( this_beaming, SCM_EOL);
278 SCM s = scm_cdr (this_beaming);
279 for (; scm_is_pair (s); s = scm_cdr (s))
281 int np = - this_dir * scm_to_int (scm_car (s));
282 scm_set_car_x (s, scm_int2num (np));
283 last_int.add_point (np);
287 if (i == stems.size () -1)
289 scm_set_cdr_x (this_beaming, SCM_EOL);
292 if (scm_ilength (scm_cdr (this_beaming)) > 0)
294 last_beaming = this_beaming;
302 TODO: should not make beams per stem, but per Y-level.
304 MAKE_SCHEME_CALLBACK (Beam, print, 1);
306 Beam::print (SCM grob)
308 Spanner *me = unsmob_spanner (grob);
311 Link_array<Grob> stems =
312 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
313 Grob* xcommon = common_refpoint_of_array (stems, me, X_AXIS);
315 xcommon = me->get_bound (LEFT)->common_refpoint (xcommon, X_AXIS);
316 xcommon = me->get_bound (RIGHT)->common_refpoint (xcommon, X_AXIS);
319 if (visible_stem_count (me))
321 // ugh -> use commonx
322 x0 = first_visible_stem (me)->relative_coordinate (xcommon, X_AXIS);
323 dx = last_visible_stem (me)->relative_coordinate (xcommon, X_AXIS) - x0;
327 x0 = stems[0]->relative_coordinate (xcommon, X_AXIS);
328 dx = stems.top ()->relative_coordinate (xcommon, X_AXIS) - x0;
331 SCM posns = me->get_property ("positions");
332 Drul_array<Real> pos;
333 if (!is_number_pair (posns))
335 programming_error ("No beam posns");
336 pos = Interval (0,0);
339 pos = ly_scm2realdrul (posns);
341 scale_drul (&pos, Staff_symbol_referencer::staff_space (me));
343 Real dy = pos[RIGHT] - pos[LEFT];
344 Real slope = (dy && dx) ? dy/dx : 0;
346 Real thick = get_thickness (me);
347 Real bdy = get_beam_translation (me);
349 SCM last_beaming = SCM_EOL;
350 Real last_xposn = -1;
351 Real last_stem_width = -1 ;
353 Real gap_length = robust_scm2double (me->get_property ("gap"), 0.0);
356 Real lt = me->get_layout ()->get_dimension (ly_symbol2scm ("linethickness"));
358 for (int i = 0; i<= stems.size (); i++)
360 Grob * st = (i < stems.size ()) ? stems[i] : 0;
362 SCM this_beaming = st ? st->get_property ("beaming") : SCM_EOL;
363 Real xposn = st ? st->relative_coordinate (xcommon, X_AXIS) : 0.0;
364 Real stem_width = st ? robust_scm2double (st->get_property ("thickness"), 1.0) *lt : 0 ;
365 Direction stem_dir = st ? to_dir (st->get_property ("direction")) : CENTER;
367 We do the space left of ST, with lfliebertjes pointing to the
368 right from the left stem, and rfliebertjes pointing left from
371 SCM left = (i > 0) ? scm_cdr (last_beaming) : SCM_EOL;
372 SCM right = st ? scm_car (this_beaming) : SCM_EOL;
374 Array<int> full_beams;
375 Array<int> lfliebertjes;
376 Array<int> rfliebertjes;
379 scm_is_pair (s); s = scm_cdr (s))
381 int b = scm_to_int (scm_car (s));
382 if (scm_c_memq (scm_car (s), right) != SCM_BOOL_F)
388 lfliebertjes.push (b);
392 scm_is_pair (s); s = scm_cdr (s))
394 int b = scm_to_int (scm_car (s));
395 if (scm_c_memq (scm_car (s), left) == SCM_BOOL_F)
397 rfliebertjes.push (b);
402 how much to stick out for beams across linebreaks
404 Real break_overshoot = 3.0;
405 Real w = (i > 0 && st) ? (xposn - last_xposn) : break_overshoot;
407 Real stem_offset = 0.0;
410 w += last_stem_width / 2;
411 stem_offset = -last_stem_width / 2;
418 Real blot = me->get_layout ()->get_dimension (ly_symbol2scm ("blotdiameter"));
419 Stencil whole = Lookup::beam (slope, w, thick, blot);
423 if (scm_is_number (me->get_property ("gap-count")))
425 gap_count = scm_to_int (me->get_property ("gap-count"));
426 gapped = Lookup::beam (slope, w - 2 * gap_length, thick, blot);
428 full_beams.sort (default_compare);
430 full_beams.reverse ();
434 for (int j = full_beams.size (); j--;)
441 b.translate_axis (gap_length, X_AXIS);
443 b.translate_axis (last_xposn - x0 + stem_offset, X_AXIS);
444 b.translate_axis (slope * (last_xposn - x0) + bdy * full_beams[j], Y_AXIS);
446 the_beam.add_stencil (b);
449 if (lfliebertjes.size () || rfliebertjes.size ())
455 int t = Stem::duration_log (st);
457 SCM proc = me->get_property ("flag-width-function");
458 SCM result = scm_call_1 (proc, scm_int2num (t));
459 nw_f = scm_to_double (result);
462 nw_f = break_overshoot / 2;
464 /* Half beam should be one note-width,
465 but let's make sure two half-beams never touch */
469 rw = nw_f <? ( (xposn - last_xposn) / 2);
472 TODO: 0.5 is a guess.
474 rw = xposn - me->get_bound (LEFT)->extent (xcommon, X_AXIS)[RIGHT]
478 lw = nw_f <? ( (xposn - last_xposn) / 2);
480 lw = me->get_bound (RIGHT)->relative_coordinate (xcommon, X_AXIS)
483 Stencil rhalf = Lookup::beam (slope, rw, thick, blot);
484 Stencil lhalf = Lookup::beam (slope, lw, thick, blot);
485 for (int j = lfliebertjes.size (); j--;)
488 b.translate_axis (last_xposn - x0, X_AXIS);
489 b.translate_axis (slope * (last_xposn-x0) + bdy * lfliebertjes[j], Y_AXIS);
490 the_beam.add_stencil (b);
492 for (int j = rfliebertjes.size (); j--;)
495 b.translate_axis (xposn - x0 - rw , X_AXIS);
496 b.translate_axis (slope * (xposn-x0 -rw) + bdy * rfliebertjes[j], Y_AXIS);
497 the_beam.add_stencil (b);
503 last_stem_width = stem_width;
504 last_beaming = this_beaming;
507 the_beam.translate_axis (x0 - me->relative_coordinate (xcommon, X_AXIS), X_AXIS);
508 the_beam.translate_axis (pos[LEFT], Y_AXIS);
511 SCM quant_score = me->get_property ("quant-score");
512 if (to_boolean (me->get_layout ()->lookup_variable (ly_symbol2scm ("debug-beam-quanting")))
513 && scm_is_string (quant_score))
517 This code prints the demerits for each beam. Perhaps this
518 should be switchable for those who want to twiddle with the
522 SCM properties = Font_interface::text_font_alist_chain (me);
524 Direction stem_dir = stems.size() ? to_dir (stems[0]->get_property ("direction")) : UP;
526 Stencil tm = *unsmob_stencil (Text_interface::interpret_markup
527 (me->get_layout ()->self_scm (), properties, quant_score));
528 the_beam.add_at_edge (Y_AXIS, stem_dir, tm, 1.0, 0);
532 return the_beam.smobbed_copy ();
539 Beam::get_default_dir (Grob *me)
541 Drul_array<int> total;
542 total[UP] = total[DOWN] = 0;
543 Drul_array<int> count;
544 count[UP] = count[DOWN] = 0;
547 Link_array<Grob> stems =
548 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
550 for (int i = 0; i < stems.size (); i++)
553 Direction sd = get_grob_direction (s);
555 int center_distance = int (- d * Stem::head_positions (s) [-d]) >? 0;
556 int current = sd ? (1 + d * sd)/2 : center_distance;
563 } while (flip (&d) != DOWN);
565 SCM func = me->get_property ("dir-function");
566 SCM s = scm_call_2 (func,
567 scm_cons (scm_int2num (count[UP]),
568 scm_int2num (count[DOWN])),
569 scm_cons (scm_int2num (total[UP]),
570 scm_int2num (total[DOWN])));
572 if (scm_is_number (s) && scm_to_int (s))
575 /* If dir is not determined: get default */
576 return to_dir (me->get_property ("neutral-direction"));
580 /* Set all stems with non-forced direction to beam direction.
581 Urg: non-forced should become `without/with unforced' direction,
582 once stem gets cleaned-up. */
584 Beam::set_stem_directions (Grob *me, Direction d)
586 Link_array<Grob> stems
587 = Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
589 for (int i = 0; i <stems.size (); i++)
593 SCM forcedir = s->get_property ("direction");
594 if (!to_dir (forcedir))
595 set_grob_direction (s, d);
600 Only try horizontal beams for knees. No reliable detection of
601 anything else is possible here, since we don't know funky-beaming
602 settings, or X-distances (slopes!) People that want sloped
603 knee-beams, should set the directions manually.
606 Beam::consider_auto_knees (Grob* me)
608 SCM scm = me->get_property ("auto-knee-gap");
609 if (!scm_is_number (scm))
612 Real threshold = scm_to_double (scm);
618 Link_array<Grob> stems =
619 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
621 Grob *common = common_refpoint_of_array (stems, me, Y_AXIS);
622 Real staff_space = Staff_symbol_referencer::staff_space (me);
624 Array<Interval> hps_array;
625 for (int i = 0; i < stems.size (); i++)
627 Grob* stem = stems[i];
628 if (Stem::is_invisible (stem))
631 Interval hps = Stem::head_positions (stem);
632 if (!hps.is_empty ())
636 hps *= staff_space * 0.5 ;
639 We could subtract beam Y position, but this routine only
640 sets stem directions, a constant shift does not have an
644 hps += stem->relative_coordinate (common, Y_AXIS);
646 if (to_dir (stem->get_property ("direction")))
648 Direction stemdir = to_dir (stem->get_property ("direction"));
649 hps[-stemdir] = - stemdir * infinity_f;
652 hps_array.push (hps);
654 gaps.remove_interval (hps);
658 Real max_gap_len = 0.0;
660 for (int i = gaps.allowed_regions_.size () -1; i >= 0 ; i--)
662 Interval gap = gaps.allowed_regions_[i];
665 the outer gaps are not knees.
667 if (isinf (gap[LEFT]) || isinf (gap[RIGHT]))
670 if (gap.length () >= max_gap_len)
672 max_gap_len = gap.length ();
677 if (max_gap_len > threshold)
680 for (int i = 0; i < stems.size (); i++)
682 Grob* stem = stems[i];
683 if (Stem::is_invisible (stem))
686 Interval hps = hps_array[j++];
689 Direction d = (hps.center () < max_gap.center ()) ?
692 stem->set_property ("direction", scm_int2num (d));
694 hps.intersect (max_gap);
695 assert (hps.is_empty () || hps.length () < 1e-6 );
702 /* Set stem's shorten property if unset.
705 take some y-position (chord/beam/nearest?) into account
706 scmify forced-fraction
708 This is done in beam because the shorten has to be uniform over the
713 Beam::set_stem_shorten (Grob *me)
716 shortening looks silly for x staff beams
721 Real forced_fraction = 1.0 * forced_stem_count (me)
722 / visible_stem_count (me);
724 int beam_count = get_beam_count (me);
726 SCM shorten_list = me->get_property ("beamed-stem-shorten");
727 if (shorten_list == SCM_EOL)
730 Real staff_space = Staff_symbol_referencer::staff_space (me);
733 robust_list_ref (beam_count -1, shorten_list);
734 Real shorten_f = scm_to_double (shorten_elt) * staff_space;
736 /* your similar cute comment here */
737 shorten_f *= forced_fraction;
740 me->set_property ("shorten", scm_make_real (shorten_f));
743 /* Call list of y-dy-callbacks, that handle setting of
747 MAKE_SCHEME_CALLBACK (Beam, after_line_breaking, 1);
749 Beam::after_line_breaking (SCM smob)
751 Grob *me = unsmob_grob (smob);
754 return SCM_UNSPECIFIED;
758 Beam::position_beam (Grob *me)
762 if (to_boolean (me->get_property ("positioning-done")))
765 me->set_property ("positioning-done", SCM_BOOL_T);
767 /* Copy to mutable list. */
768 SCM s = ly_deep_copy (me->get_property ("positions"));
769 me->set_property ("positions", s);
771 if (scm_car (s) == SCM_BOOL_F)
773 // one wonders if such genericity is necessary --hwn.
774 SCM callbacks = me->get_property ("position-callbacks");
775 for (SCM i = callbacks; scm_is_pair (i); i = scm_cdr (i))
776 scm_call_1 (scm_car (i), me->self_scm ());
779 set_stem_lengths (me);
784 set_minimum_dy (Grob *me, Real * dy)
789 If dy is smaller than the smallest quant, we
790 get absurd direction-sign penalties.
793 Real ss = Staff_symbol_referencer::staff_space (me);
794 Real thickness = Beam::get_thickness (me) / ss ;
795 Real slt = Staff_symbol_referencer::line_thickness (me) / ss;
796 Real sit = (thickness - slt) / 2;
798 Real hang = 1.0 - (thickness - slt) / 2;
800 *dy = sign (*dy) * (fabs (*dy)
802 (sit <? inter <? hang));
807 Compute a first approximation to the beam slope.
809 MAKE_SCHEME_CALLBACK (Beam, least_squares, 1);
811 Beam::least_squares (SCM smob)
813 Grob *me = unsmob_grob (smob);
815 int count = visible_stem_count (me);
820 me->set_property ("positions", ly_interval2scm (pos));
821 return SCM_UNSPECIFIED;
825 Array<Real> x_posns ;
826 Link_array<Grob> stems =
827 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
828 Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
829 Grob *commony = common_refpoint_of_array (stems, me, Y_AXIS);
831 Real my_y = me->relative_coordinate (commony, Y_AXIS);
833 Grob *fvs = first_visible_stem (me);
834 Grob *lvs = last_visible_stem (me);
836 Interval ideal (Stem::get_stem_info (fvs).ideal_y_
837 + fvs->relative_coordinate (commony, Y_AXIS) -my_y,
838 Stem::get_stem_info (lvs).ideal_y_
839 + lvs->relative_coordinate (commony, Y_AXIS) - my_y);
841 Real x0 = first_visible_stem (me)->relative_coordinate (commonx, X_AXIS);
842 for (int i = 0; i < stems.size (); i++)
846 Real x = s->relative_coordinate (commonx, X_AXIS) - x0;
849 Real dx = last_visible_stem (me)->relative_coordinate (commonx, X_AXIS) - x0;
858 Interval chord (Stem::chord_start_y (first_visible_stem (me)),
859 Stem::chord_start_y (last_visible_stem (me)));
861 /* Simple beams (2 stems) on middle line should be allowed to be
864 However, if both stems reach middle line,
865 ideal[LEFT] == ideal[RIGHT] and ideal.delta () == 0.
867 For that case, we apply artificial slope */
868 if (!ideal[LEFT] && chord.delta () && count == 2)
871 Direction d = (Direction) (sign (chord.delta ()) * UP);
872 pos[d] = get_thickness (me) / 2;
881 For broken beams this doesn't work well. In this case, the
882 slope esp. of the first part of a broken beam should predict
883 where the second part goes.
885 me->set_property ("least-squares-dy",
886 scm_make_real (pos[RIGHT] - pos[LEFT]));
890 Array<Offset> ideals;
891 for (int i = 0; i < stems.size (); i++)
894 if (Stem::is_invisible (s))
896 ideals.push (Offset (x_posns[i],
897 Stem::get_stem_info (s).ideal_y_
898 + s->relative_coordinate (commony, Y_AXIS)
902 minimise_least_squares (&slope, &y, ideals);
906 set_minimum_dy (me,&dy);
907 me->set_property ("least-squares-dy", scm_make_real (dy));
908 pos = Interval (y, (y+dy));
912 "position" is relative to the staff.
914 scale_drul (&pos, 1/ Staff_symbol_referencer::staff_space (me));
916 me->set_property ("positions", ly_interval2scm (pos));
918 return SCM_UNSPECIFIED;
923 We can't combine with previous function, since check concave and
924 slope damping comes first.
926 TODO: we should use the concaveness to control the amount of damping
930 MAKE_SCHEME_CALLBACK (Beam, shift_region_to_valid, 1);
932 Beam::shift_region_to_valid (SCM grob)
934 Grob *me = unsmob_grob (grob);
938 Array<Real> x_posns ;
939 Link_array<Grob> stems =
940 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
941 Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
942 Grob *commony = common_refpoint_of_array (stems, me, Y_AXIS);
944 Grob *fvs = first_visible_stem (me);
947 return SCM_UNSPECIFIED;
949 Real x0 = fvs->relative_coordinate (commonx, X_AXIS);
950 for (int i = 0; i < stems.size (); i++)
954 Real x = s->relative_coordinate (commonx, X_AXIS) - x0;
958 Grob *lvs = last_visible_stem (me);
960 return SCM_UNSPECIFIED;
962 Real dx = lvs->relative_coordinate (commonx, X_AXIS) - x0;
964 Drul_array<Real> pos = ly_scm2interval ( me->get_property ("positions"));
966 scale_drul (&pos, Staff_symbol_referencer::staff_space (me));
968 Real dy = pos[RIGHT] - pos[LEFT];
974 Shift the positions so that we have a chance of finding good
975 quants (i.e. no short stem failures.)
977 Interval feasible_left_point;
978 feasible_left_point.set_full ();
979 for (int i = 0; i < stems.size (); i++)
982 if (Stem::is_invisible (s))
985 Direction d = Stem::get_direction (s);
988 Stem::get_stem_info (s).shortest_y_
989 - slope * x_posns [i];
992 left_y is now relative to the stem S. We want relative to
993 ourselves, so translate:
996 + s->relative_coordinate (commony, Y_AXIS)
997 - me->relative_coordinate (commony, Y_AXIS);
1003 feasible_left_point.intersect (flp);
1006 if (feasible_left_point.is_empty ())
1007 warning (_ ("no viable initial configuration found: may not find good beam slope"));
1008 else if (!feasible_left_point.contains (y))
1010 if (isinf (feasible_left_point[DOWN]))
1011 y = feasible_left_point[UP] - REGION_SIZE;
1012 else if (isinf (feasible_left_point[UP]))
1013 y = feasible_left_point[DOWN]+ REGION_SIZE;
1015 y = feasible_left_point.center ();
1018 pos = Drul_array<Real> (y, (y+dy));
1019 scale_drul (&pos, 1/ Staff_symbol_referencer::staff_space (me));
1021 me->set_property ("positions", ly_interval2scm (pos));
1022 return SCM_UNSPECIFIED;
1025 /* This neat trick is by Werner Lemberg,
1026 damped = tanh (slope)
1027 corresponds with some tables in [Wanske] CHECKME */
1028 MAKE_SCHEME_CALLBACK (Beam, slope_damping, 1);
1030 Beam::slope_damping (SCM smob)
1032 Grob *me = unsmob_grob (smob);
1034 if (visible_stem_count (me) <= 1)
1035 return SCM_UNSPECIFIED;
1037 SCM s = me->get_property ("damping");
1038 Real damping = scm_to_double (s);
1042 Drul_array<Real> pos = ly_scm2interval (me->get_property ("positions"));
1043 scale_drul (&pos, Staff_symbol_referencer::staff_space (me));
1045 Real dy = pos[RIGHT] - pos[LEFT];
1047 Grob *fvs = first_visible_stem (me);
1048 Grob *lvs = last_visible_stem (me);
1050 Grob *commonx = fvs->common_refpoint (lvs, X_AXIS);
1053 Real dx = last_visible_stem (me)->relative_coordinate (commonx, X_AXIS)
1054 - first_visible_stem (me)->relative_coordinate (commonx, X_AXIS);
1056 Real slope = dy && dx ? dy/dx : 0;
1058 Real concaveness = robust_scm2double (me->get_property ("concaveness"), 0.0);
1060 slope = 0.6 * tanh (slope) / (damping + concaveness);
1062 Real damped_dy = slope * dx;
1064 set_minimum_dy (me, &damped_dy);
1066 pos[LEFT] += (dy - damped_dy) / 2;
1067 pos[RIGHT] -= (dy - damped_dy) / 2;
1069 scale_drul (&pos, 1/Staff_symbol_referencer::staff_space (me));
1071 me->set_property ("positions", ly_interval2scm (pos));
1073 return SCM_UNSPECIFIED;
1077 Report slice containing the numbers that are both in (car BEAMING)
1081 where_are_the_whole_beams (SCM beaming)
1085 for ( SCM s = scm_car (beaming); scm_is_pair (s) ; s = scm_cdr (s))
1087 if (scm_c_memq (scm_car (s), scm_cdr (beaming)) != SCM_BOOL_F)
1089 l.add_point (scm_to_int (scm_car (s)));
1095 /* Return the Y position of the stem-end, given the Y-left, Y-right
1096 in POS for stem S. This Y position is relative to S. */
1098 Beam::calc_stem_y (Grob *me, Grob* s, Grob ** common,
1100 Drul_array<Real> pos, bool french)
1102 Real beam_translation = get_beam_translation (me);
1105 Real r = s->relative_coordinate (common[X_AXIS], X_AXIS) - xl;
1106 Real dy = pos[RIGHT] - pos[LEFT];
1108 Real stem_y_beam0 = (dy && dx
1113 Direction my_dir = get_grob_direction (s);
1114 SCM beaming = s->get_property ("beaming");
1116 Real stem_y = stem_y_beam0;
1119 Slice bm = where_are_the_whole_beams (beaming);
1120 if (!bm.is_empty ())
1121 stem_y += beam_translation * bm[-my_dir];
1125 Slice bm = Stem::beam_multiplicity (s);
1126 if (!bm.is_empty ())
1127 stem_y += bm[my_dir] * beam_translation;
1130 Real id = me->relative_coordinate (common[Y_AXIS], Y_AXIS)
1131 - s->relative_coordinate (common[Y_AXIS], Y_AXIS);
1137 Hmm. At this time, beam position and slope are determined. Maybe,
1138 stem directions and length should set to relative to the chord's
1139 position of the beam. */
1141 Beam::set_stem_lengths (Grob *me)
1143 Link_array<Grob> stems =
1144 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
1150 for (int a = 2; a--;)
1151 common[a] = common_refpoint_of_array (stems, me, Axis (a));
1153 Drul_array<Real> pos = ly_scm2realdrul (me->get_property ("positions"));
1154 Real staff_space = Staff_symbol_referencer::staff_space (me);
1155 scale_drul (&pos, staff_space);
1159 if (scm_is_number (me->get_property ("gap-count"))
1160 &&scm_to_int (me->get_property ("gap-count")))
1163 thick = get_thickness (me);
1166 // ugh -> use commonx
1167 Grob * fvs = first_visible_stem (me);
1168 Grob *lvs = last_visible_stem (me);
1170 Real xl = fvs ? fvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
1171 Real xr = lvs ? lvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
1173 for (int i = 0; i < stems.size (); i++)
1176 if (Stem::is_invisible (s))
1179 bool french = to_boolean (s->get_property ("french-beaming"));
1180 Real stem_y = calc_stem_y (me, s, common,
1182 pos, french && s != lvs && s!= fvs);
1185 Make the stems go up to the end of the beam. This doesn't matter
1186 for normal beams, but for tremolo beams it looks silly otherwise.
1189 stem_y += thick * 0.5 * get_grob_direction (s);
1191 Stem::set_stemend (s, 2* stem_y / staff_space);
1196 Beam::set_beaming (Grob *me, Beaming_info_list *beaming)
1198 Link_array<Grob> stems =
1199 Pointer_group_interface__extract_grobs (me, (Grob *)0, "stems");
1202 for (int i = 0; i < stems.size (); i++)
1205 Don't overwrite user settings.
1210 /* Don't set beaming for outside of outer stems */
1211 if ( (d == LEFT && i == 0)
1212 || (d == RIGHT && i == stems.size () -1))
1215 Grob *st = stems[i];
1216 SCM beaming_prop = st->get_property ("beaming");
1217 if (beaming_prop == SCM_EOL ||
1218 index_get_cell (beaming_prop, d) == SCM_EOL)
1220 int b = beaming->infos_.elem (i).beams_i_drul_[d];
1222 && i < stems.size () -1
1223 && Stem::is_invisible (st))
1224 b = b <? beaming->infos_.elem (i).beams_i_drul_[-d];
1226 Stem::set_beaming (st, b, d);
1229 while (flip (&d) != LEFT);
1234 Beam::forced_stem_count (Grob *me)
1236 Link_array<Grob>stems =
1237 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1239 for (int i = 0; i < stems.size (); i++)
1243 if (Stem::is_invisible (s))
1246 /* I can imagine counting those boundaries as a half forced stem,
1247 but let's count them full for now. */
1248 if (abs (Stem::chord_start_y (s)) > 0.1
1249 && (Stem::get_direction (s) != Stem::get_default_dir (s)))
1259 Beam::visible_stem_count (Grob *me)
1261 Link_array<Grob>stems =
1262 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1264 for (int i = stems.size (); i--;)
1266 if (!Stem::is_invisible (stems[i]))
1273 Beam::first_visible_stem (Grob *me)
1275 Link_array<Grob>stems =
1276 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1278 for (int i = 0; i < stems.size (); i++)
1280 if (!Stem::is_invisible (stems[i]))
1287 Beam::last_visible_stem (Grob *me)
1289 Link_array<Grob>stems =
1290 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1291 for (int i = stems.size (); i--;)
1293 if (!Stem::is_invisible (stems[i]))
1303 handle rest under beam (do_post: beams are calculated now)
1304 what about combination of collisions and rest under beam.
1308 rest -> stem -> beam -> interpolate_y_position ()
1310 MAKE_SCHEME_CALLBACK (Beam, rest_collision_callback, 2);
1312 Beam::rest_collision_callback (SCM element_smob, SCM axis)
1314 Grob *rest = unsmob_grob (element_smob);
1315 Axis a = (Axis) scm_to_int (axis);
1317 if (scm_is_number (rest->get_property ("staff-position")))
1318 return scm_int2num (0);
1320 assert (a == Y_AXIS);
1322 Grob *st = unsmob_grob (rest->get_property ("stem"));
1325 return scm_make_real (0.0);
1326 Grob *beam = unsmob_grob (stem->get_property ("beam"));
1328 || !Beam::has_interface (beam)
1329 || !Beam::visible_stem_count (beam))
1330 return scm_make_real (0.0);
1332 Drul_array<Real> pos (0, 0);
1333 SCM s = beam->get_property ("positions");
1334 if (scm_is_pair (s) && scm_is_number (scm_car (s)))
1335 pos = ly_scm2interval (s);
1336 Real staff_space = Staff_symbol_referencer::staff_space (rest);
1338 scale_drul (&pos, staff_space);
1341 Real dy = pos[RIGHT] - pos[LEFT];
1343 // ugh -> use commonx
1344 Real x0 = first_visible_stem (beam)->relative_coordinate (0, X_AXIS);
1345 Real dx = last_visible_stem (beam)->relative_coordinate (0, X_AXIS) - x0;
1346 Real slope = dy && dx ? dy/dx : 0;
1348 Direction d = Stem::get_direction (stem);
1349 Real stem_y = pos[LEFT] + (stem->relative_coordinate (0, X_AXIS) - x0) * slope;
1351 Real beam_translation = get_beam_translation (beam);
1352 Real beam_thickness = Beam::get_thickness (beam);
1355 TODO: this is not strictly correct for 16th knee beams.
1358 Stem::beam_multiplicity (stem).length() + 1;
1360 Real height_of_my_beams = beam_thickness / 2
1361 + (beam_count - 1) * beam_translation;
1362 Real beam_y = stem_y - d * height_of_my_beams;
1364 Grob *common_y = rest->common_refpoint (beam, Y_AXIS);
1366 Real rest_dim = rest->extent (common_y, Y_AXIS)[d];
1367 Real minimum_distance =
1368 + staff_space * (robust_scm2double (stem->get_property ("stemlet-length"), 0.0)
1369 + robust_scm2double (rest->get_property ("minimum-distance"), 0.0));
1371 Real shift = d * ( ((beam_y - d * minimum_distance) - rest_dim) * d <? 0.0);
1373 shift /= staff_space;
1374 Real rad = Staff_symbol_referencer::line_count (rest) * staff_space / 2;
1376 /* Always move discretely by half spaces */
1377 shift = ceil (fabs (shift * 2.0)) / 2.0 * sign (shift);
1379 /* Inside staff, move by whole spaces*/
1380 if ( (rest->extent (common_y, Y_AXIS)[d] + staff_space * shift) * d
1382 || (rest->extent (common_y, Y_AXIS)[-d] + staff_space * shift) * -d
1384 shift = ceil (fabs (shift)) *sign (shift);
1386 return scm_make_real (staff_space * shift);
1390 Beam::is_knee (Grob* me)
1392 SCM k = me->get_property ("knee");
1393 if (scm_is_bool (k))
1394 return ly_scm2bool (k);
1398 for (SCM s = me->get_property ("stems"); scm_is_pair (s); s = scm_cdr (s))
1400 Direction dir = get_grob_direction (unsmob_grob (scm_car (s)));
1409 me->set_property ("knee", ly_bool2scm (knee));
1415 Beam::get_direction_beam_count (Grob *me, Direction d )
1417 Link_array<Grob>stems =
1418 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1421 for (int i = stems.size (); i--;)
1424 Should we take invisible stems into account?
1426 if (Stem::get_direction (stems[i]) == d)
1427 bc = bc >? (Stem::beam_multiplicity (stems[i]).length () + 1);
1434 ADD_INTERFACE (Beam, "beam-interface",
1436 "The @code{thickness} property is the weight of beams, and is measured "
1439 "knee positioning-done position-callbacks "
1440 "concaveness dir-function quant-score auto-knee-gap gap "
1441 "gap-count chord-tremolo beamed-stem-shorten shorten least-squares-dy "
1442 "damping inspect-quants flag-width-function neutral-direction positions space-function "