2 beam.cc -- implement Beam
4 source file of the GNU LilyPond music typesetter
6 (c) 1997--2005 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 extract_grob_array (me, ly_symbol2scm ("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 extract_grob_array (me, ly_symbol2scm ("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 extract_grob_array (me, ly_symbol2scm ("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 = extract_grob_array (me, ly_symbol2scm ("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))
616 Link_array<Grob> stems =
617 extract_grob_array (me, ly_symbol2scm ("stems"));
619 Grob *common = common_refpoint_of_array (stems, me, Y_AXIS);
620 Real staff_space = Staff_symbol_referencer::staff_space (me);
622 Array<Interval> head_positions_array;
623 for (int i = 0; i < stems.size (); i++)
625 Grob* stem = stems[i];
626 if (Stem::is_invisible (stem))
629 Interval head_positions = Stem::head_positions (stem);
630 if (!head_positions.is_empty ())
632 head_positions[LEFT] += -1;
633 head_positions[RIGHT] += 1;
634 head_positions *= staff_space * 0.5 ;
637 We could subtract beam Y position, but this routine only
638 sets stem directions, a constant shift does not have an
641 head_positions += stem->relative_coordinate (common, Y_AXIS);
643 if (to_dir (stem->get_property ("direction")))
645 Direction stemdir = to_dir (stem->get_property ("direction"));
646 head_positions[-stemdir] = - stemdir * infinity_f;
649 head_positions_array.push (head_positions);
651 gaps.remove_interval (head_positions);
655 Real max_gap_len = 0.0;
657 for (int i = gaps.allowed_regions_.size () -1; i >= 0 ; i--)
659 Interval gap = gaps.allowed_regions_[i];
662 the outer gaps are not knees.
664 if (isinf (gap[LEFT]) || isinf (gap[RIGHT]))
667 if (gap.length () >= max_gap_len)
669 max_gap_len = gap.length ();
674 Real beam_translation = get_beam_translation (me);
675 Real beam_thickness = Beam::get_thickness (me);
676 int beam_count = Beam::get_beam_count (me);
677 Real height_of_beams = beam_thickness / 2
678 + (beam_count - 1) * beam_translation;
679 Real threshold = scm_to_double (scm) + height_of_beams;
681 if (max_gap_len > threshold)
684 for (int i = 0; i < stems.size (); i++)
686 Grob* stem = stems[i];
687 if (Stem::is_invisible (stem))
690 Interval head_positions = head_positions_array[j++];
693 Direction d = (head_positions.center () < max_gap.center ()) ?
696 stem->set_property ("direction", scm_int2num (d));
698 head_positions.intersect (max_gap);
699 assert (head_positions.is_empty () || head_positions.length () < 1e-6 );
706 /* Set stem's shorten property if unset.
709 take some y-position (chord/beam/nearest?) into account
710 scmify forced-fraction
712 This is done in beam because the shorten has to be uniform over the
717 Beam::set_stem_shorten (Grob *me)
720 shortening looks silly for x staff beams
725 Real forced_fraction = 1.0 * forced_stem_count (me)
726 / visible_stem_count (me);
728 int beam_count = get_beam_count (me);
730 SCM shorten_list = me->get_property ("beamed-stem-shorten");
731 if (shorten_list == SCM_EOL)
734 Real staff_space = Staff_symbol_referencer::staff_space (me);
737 robust_list_ref (beam_count -1, shorten_list);
738 Real shorten_f = scm_to_double (shorten_elt) * staff_space;
740 /* your similar cute comment here */
741 shorten_f *= forced_fraction;
744 me->set_property ("shorten", scm_make_real (shorten_f));
747 /* Call list of y-dy-callbacks, that handle setting of
751 MAKE_SCHEME_CALLBACK (Beam, after_line_breaking, 1);
753 Beam::after_line_breaking (SCM smob)
755 Grob *me = unsmob_grob (smob);
758 return SCM_UNSPECIFIED;
762 Beam::position_beam (Grob *me)
766 if (to_boolean (me->get_property ("positioning-done")))
769 me->set_property ("positioning-done", SCM_BOOL_T);
771 /* Copy to mutable list. */
772 SCM s = ly_deep_copy (me->get_property ("positions"));
773 me->set_property ("positions", s);
775 if (scm_car (s) == SCM_BOOL_F)
777 // one wonders if such genericity is necessary --hwn.
778 SCM callbacks = me->get_property ("position-callbacks");
779 for (SCM i = callbacks; scm_is_pair (i); i = scm_cdr (i))
780 scm_call_1 (scm_car (i), me->self_scm ());
783 set_stem_lengths (me);
788 set_minimum_dy (Grob *me, Real * dy)
793 If dy is smaller than the smallest quant, we
794 get absurd direction-sign penalties.
797 Real ss = Staff_symbol_referencer::staff_space (me);
798 Real thickness = Beam::get_thickness (me) / ss ;
799 Real slt = Staff_symbol_referencer::line_thickness (me) / ss;
800 Real sit = (thickness - slt) / 2;
802 Real hang = 1.0 - (thickness - slt) / 2;
804 *dy = sign (*dy) * (fabs (*dy)
806 (sit <? inter <? hang));
811 Compute a first approximation to the beam slope.
813 MAKE_SCHEME_CALLBACK (Beam, least_squares, 1);
815 Beam::least_squares (SCM smob)
817 Grob *me = unsmob_grob (smob);
819 int count = visible_stem_count (me);
824 me->set_property ("positions", ly_interval2scm (pos));
825 return SCM_UNSPECIFIED;
829 Array<Real> x_posns ;
830 Link_array<Grob> stems =
831 extract_grob_array (me, ly_symbol2scm ("stems"));
832 Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
833 Grob *commony = common_refpoint_of_array (stems, me, Y_AXIS);
835 Real my_y = me->relative_coordinate (commony, Y_AXIS);
837 Grob *fvs = first_visible_stem (me);
838 Grob *lvs = last_visible_stem (me);
840 Interval ideal (Stem::get_stem_info (fvs).ideal_y_
841 + fvs->relative_coordinate (commony, Y_AXIS) -my_y,
842 Stem::get_stem_info (lvs).ideal_y_
843 + lvs->relative_coordinate (commony, Y_AXIS) - my_y);
845 Real x0 = first_visible_stem (me)->relative_coordinate (commonx, X_AXIS);
846 for (int i = 0; i < stems.size (); i++)
850 Real x = s->relative_coordinate (commonx, X_AXIS) - x0;
853 Real dx = last_visible_stem (me)->relative_coordinate (commonx, X_AXIS) - x0;
862 Interval chord (Stem::chord_start_y (first_visible_stem (me)),
863 Stem::chord_start_y (last_visible_stem (me)));
865 /* Simple beams (2 stems) on middle line should be allowed to be
868 However, if both stems reach middle line,
869 ideal[LEFT] == ideal[RIGHT] and ideal.delta () == 0.
871 For that case, we apply artificial slope */
872 if (!ideal[LEFT] && chord.delta () && count == 2)
875 Direction d = (Direction) (sign (chord.delta ()) * UP);
876 pos[d] = get_thickness (me) / 2;
885 For broken beams this doesn't work well. In this case, the
886 slope esp. of the first part of a broken beam should predict
887 where the second part goes.
889 me->set_property ("least-squares-dy",
890 scm_make_real (pos[RIGHT] - pos[LEFT]));
894 Array<Offset> ideals;
895 for (int i = 0; i < stems.size (); i++)
898 if (Stem::is_invisible (s))
900 ideals.push (Offset (x_posns[i],
901 Stem::get_stem_info (s).ideal_y_
902 + s->relative_coordinate (commony, Y_AXIS)
906 minimise_least_squares (&slope, &y, ideals);
910 set_minimum_dy (me, &dy);
911 me->set_property ("least-squares-dy", scm_make_real (dy));
912 pos = Interval (y, (y+dy));
916 "position" is relative to the staff.
918 scale_drul (&pos, 1/ Staff_symbol_referencer::staff_space (me));
920 me->set_property ("positions", ly_interval2scm (pos));
922 return SCM_UNSPECIFIED;
927 We can't combine with previous function, since check concave and
928 slope damping comes first.
930 TODO: we should use the concaveness to control the amount of damping
934 MAKE_SCHEME_CALLBACK (Beam, shift_region_to_valid, 1);
936 Beam::shift_region_to_valid (SCM grob)
938 Grob *me = unsmob_grob (grob);
942 Array<Real> x_posns ;
943 Link_array<Grob> stems =
944 extract_grob_array (me, ly_symbol2scm ("stems"));
945 Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
946 Grob *commony = common_refpoint_of_array (stems, me, Y_AXIS);
948 Grob *fvs = first_visible_stem (me);
951 return SCM_UNSPECIFIED;
953 Real x0 = fvs->relative_coordinate (commonx, X_AXIS);
954 for (int i = 0; i < stems.size (); i++)
958 Real x = s->relative_coordinate (commonx, X_AXIS) - x0;
962 Grob *lvs = last_visible_stem (me);
964 return SCM_UNSPECIFIED;
966 Real dx = lvs->relative_coordinate (commonx, X_AXIS) - x0;
968 Drul_array<Real> pos = ly_scm2interval ( me->get_property ("positions"));
970 scale_drul (&pos, Staff_symbol_referencer::staff_space (me));
972 Real dy = pos[RIGHT] - pos[LEFT];
978 Shift the positions so that we have a chance of finding good
979 quants (i.e. no short stem failures.)
981 Interval feasible_left_point;
982 feasible_left_point.set_full ();
983 for (int i = 0; i < stems.size (); i++)
986 if (Stem::is_invisible (s))
989 Direction d = Stem::get_direction (s);
992 Stem::get_stem_info (s).shortest_y_
993 - slope * x_posns [i];
996 left_y is now relative to the stem S. We want relative to
997 ourselves, so translate:
1000 + s->relative_coordinate (commony, Y_AXIS)
1001 - me->relative_coordinate (commony, Y_AXIS);
1007 feasible_left_point.intersect (flp);
1010 if (feasible_left_point.is_empty ())
1011 warning (_ ("no viable initial configuration found: may not find good beam slope"));
1012 else if (!feasible_left_point.contains (y))
1014 if (isinf (feasible_left_point[DOWN]))
1015 y = feasible_left_point[UP] - REGION_SIZE;
1016 else if (isinf (feasible_left_point[UP]))
1017 y = feasible_left_point[DOWN]+ REGION_SIZE;
1019 y = feasible_left_point.center ();
1022 pos = Drul_array<Real> (y, (y+dy));
1023 scale_drul (&pos, 1/ Staff_symbol_referencer::staff_space (me));
1025 me->set_property ("positions", ly_interval2scm (pos));
1026 return SCM_UNSPECIFIED;
1029 /* This neat trick is by Werner Lemberg,
1030 damped = tanh (slope)
1031 corresponds with some tables in [Wanske] CHECKME */
1032 MAKE_SCHEME_CALLBACK (Beam, slope_damping, 1);
1034 Beam::slope_damping (SCM smob)
1036 Grob *me = unsmob_grob (smob);
1038 if (visible_stem_count (me) <= 1)
1039 return SCM_UNSPECIFIED;
1041 SCM s = me->get_property ("damping");
1042 Real damping = scm_to_double (s);
1046 Drul_array<Real> pos = ly_scm2interval (me->get_property ("positions"));
1047 scale_drul (&pos, Staff_symbol_referencer::staff_space (me));
1049 Real dy = pos[RIGHT] - pos[LEFT];
1051 Grob *fvs = first_visible_stem (me);
1052 Grob *lvs = last_visible_stem (me);
1054 Grob *commonx = fvs->common_refpoint (lvs, X_AXIS);
1057 Real dx = last_visible_stem (me)->relative_coordinate (commonx, X_AXIS)
1058 - first_visible_stem (me)->relative_coordinate (commonx, X_AXIS);
1060 Real slope = dy && dx ? dy/dx : 0;
1062 Real concaveness = robust_scm2double (me->get_property ("concaveness"), 0.0);
1064 slope = 0.6 * tanh (slope) / (damping + concaveness);
1066 Real damped_dy = slope * dx;
1068 set_minimum_dy (me, &damped_dy);
1070 pos[LEFT] += (dy - damped_dy) / 2;
1071 pos[RIGHT] -= (dy - damped_dy) / 2;
1073 scale_drul (&pos, 1/Staff_symbol_referencer::staff_space (me));
1075 me->set_property ("positions", ly_interval2scm (pos));
1077 return SCM_UNSPECIFIED;
1081 Report slice containing the numbers that are both in (car BEAMING)
1085 where_are_the_whole_beams (SCM beaming)
1089 for ( SCM s = scm_car (beaming); scm_is_pair (s) ; s = scm_cdr (s))
1091 if (scm_c_memq (scm_car (s), scm_cdr (beaming)) != SCM_BOOL_F)
1093 l.add_point (scm_to_int (scm_car (s)));
1099 /* Return the Y position of the stem-end, given the Y-left, Y-right
1100 in POS for stem S. This Y position is relative to S. */
1102 Beam::calc_stem_y (Grob *me, Grob* s, Grob ** common,
1104 Drul_array<Real> pos, bool french)
1106 Real beam_translation = get_beam_translation (me);
1109 Real r = s->relative_coordinate (common[X_AXIS], X_AXIS) - xl;
1110 Real dy = pos[RIGHT] - pos[LEFT];
1112 Real stem_y_beam0 = (dy && dx
1117 Direction my_dir = get_grob_direction (s);
1118 SCM beaming = s->get_property ("beaming");
1120 Real stem_y = stem_y_beam0;
1123 Slice bm = where_are_the_whole_beams (beaming);
1124 if (!bm.is_empty ())
1125 stem_y += beam_translation * bm[-my_dir];
1129 Slice bm = Stem::beam_multiplicity (s);
1130 if (!bm.is_empty ())
1131 stem_y += bm[my_dir] * beam_translation;
1134 Real id = me->relative_coordinate (common[Y_AXIS], Y_AXIS)
1135 - s->relative_coordinate (common[Y_AXIS], Y_AXIS);
1141 Hmm. At this time, beam position and slope are determined. Maybe,
1142 stem directions and length should set to relative to the chord's
1143 position of the beam. */
1145 Beam::set_stem_lengths (Grob *me)
1147 Link_array<Grob> stems =
1148 extract_grob_array (me, ly_symbol2scm ("stems"));
1154 for (int a = 2; a--;)
1155 common[a] = common_refpoint_of_array (stems, me, Axis (a));
1157 Drul_array<Real> pos = ly_scm2realdrul (me->get_property ("positions"));
1158 Real staff_space = Staff_symbol_referencer::staff_space (me);
1159 scale_drul (&pos, staff_space);
1163 if (scm_is_number (me->get_property ("gap-count"))
1164 &&scm_to_int (me->get_property ("gap-count")))
1167 thick = get_thickness (me);
1170 // ugh -> use commonx
1171 Grob * fvs = first_visible_stem (me);
1172 Grob *lvs = last_visible_stem (me);
1174 Real xl = fvs ? fvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
1175 Real xr = lvs ? lvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
1177 for (int i = 0; i < stems.size (); i++)
1180 if (Stem::is_invisible (s))
1183 bool french = to_boolean (s->get_property ("french-beaming"));
1184 Real stem_y = calc_stem_y (me, s, common,
1186 pos, french && s != lvs && s!= fvs);
1189 Make the stems go up to the end of the beam. This doesn't matter
1190 for normal beams, but for tremolo beams it looks silly otherwise.
1193 stem_y += thick * 0.5 * get_grob_direction (s);
1195 Stem::set_stemend (s, 2* stem_y / staff_space);
1200 Beam::set_beaming (Grob *me, Beaming_info_list *beaming)
1202 Link_array<Grob> stems =
1203 extract_grob_array (me, ly_symbol2scm ("stems"));
1206 for (int i = 0; i < stems.size (); i++)
1209 Don't overwrite user settings.
1214 /* Don't set beaming for outside of outer stems */
1215 if ( (d == LEFT && i == 0)
1216 || (d == RIGHT && i == stems.size () -1))
1219 Grob *st = stems[i];
1220 SCM beaming_prop = st->get_property ("beaming");
1221 if (beaming_prop == SCM_EOL ||
1222 index_get_cell (beaming_prop, d) == SCM_EOL)
1224 int b = beaming->infos_.elem (i).beams_i_drul_[d];
1226 && i < stems.size () -1
1227 && Stem::is_invisible (st))
1228 b = b <? beaming->infos_.elem (i).beams_i_drul_[-d];
1230 Stem::set_beaming (st, b, d);
1233 while (flip (&d) != LEFT);
1238 Beam::forced_stem_count (Grob *me)
1240 Link_array<Grob>stems =
1241 extract_grob_array (me, ly_symbol2scm ("stems"));
1243 for (int i = 0; i < stems.size (); i++)
1247 if (Stem::is_invisible (s))
1250 /* I can imagine counting those boundaries as a half forced stem,
1251 but let's count them full for now. */
1252 if (abs (Stem::chord_start_y (s)) > 0.1
1253 && (Stem::get_direction (s) != Stem::get_default_dir (s)))
1263 Beam::visible_stem_count (Grob *me)
1265 Link_array<Grob>stems =
1266 extract_grob_array (me, ly_symbol2scm ("stems"));
1268 for (int i = stems.size (); i--;)
1270 if (!Stem::is_invisible (stems[i]))
1277 Beam::first_visible_stem (Grob *me)
1279 Link_array<Grob>stems =
1280 extract_grob_array (me, ly_symbol2scm ("stems"));
1282 for (int i = 0; i < stems.size (); i++)
1284 if (!Stem::is_invisible (stems[i]))
1291 Beam::last_visible_stem (Grob *me)
1293 Link_array<Grob>stems =
1294 extract_grob_array (me, ly_symbol2scm ("stems"));
1295 for (int i = stems.size (); i--;)
1297 if (!Stem::is_invisible (stems[i]))
1307 handle rest under beam (do_post: beams are calculated now)
1308 what about combination of collisions and rest under beam.
1312 rest -> stem -> beam -> interpolate_y_position ()
1314 MAKE_SCHEME_CALLBACK (Beam, rest_collision_callback, 2);
1316 Beam::rest_collision_callback (SCM element_smob, SCM axis)
1318 Grob *rest = unsmob_grob (element_smob);
1319 Axis a = (Axis) scm_to_int (axis);
1321 if (scm_is_number (rest->get_property ("staff-position")))
1322 return scm_int2num (0);
1324 assert (a == Y_AXIS);
1326 Grob *st = unsmob_grob (rest->get_property ("stem"));
1329 return scm_make_real (0.0);
1330 Grob *beam = unsmob_grob (stem->get_property ("beam"));
1332 || !Beam::has_interface (beam)
1333 || !Beam::visible_stem_count (beam))
1334 return scm_make_real (0.0);
1336 Drul_array<Real> pos (0, 0);
1337 SCM s = beam->get_property ("positions");
1338 if (scm_is_pair (s) && scm_is_number (scm_car (s)))
1339 pos = ly_scm2interval (s);
1340 Real staff_space = Staff_symbol_referencer::staff_space (rest);
1342 scale_drul (&pos, staff_space);
1345 Real dy = pos[RIGHT] - pos[LEFT];
1347 // ugh -> use commonx
1348 Real x0 = first_visible_stem (beam)->relative_coordinate (0, X_AXIS);
1349 Real dx = last_visible_stem (beam)->relative_coordinate (0, X_AXIS) - x0;
1350 Real slope = dy && dx ? dy/dx : 0;
1352 Direction d = Stem::get_direction (stem);
1353 Real stem_y = pos[LEFT] + (stem->relative_coordinate (0, X_AXIS) - x0) * slope;
1355 Real beam_translation = get_beam_translation (beam);
1356 Real beam_thickness = Beam::get_thickness (beam);
1359 TODO: this is not strictly correct for 16th knee beams.
1362 Stem::beam_multiplicity (stem).length() + 1;
1364 Real height_of_my_beams = beam_thickness / 2
1365 + (beam_count - 1) * beam_translation;
1366 Real beam_y = stem_y - d * height_of_my_beams;
1368 Grob *common_y = rest->common_refpoint (beam, Y_AXIS);
1370 Real rest_dim = rest->extent (common_y, Y_AXIS)[d];
1371 Real minimum_distance =
1372 + staff_space * (robust_scm2double (stem->get_property ("stemlet-length"), 0.0)
1373 + robust_scm2double (rest->get_property ("minimum-distance"), 0.0));
1375 Real shift = d * ( ((beam_y - d * minimum_distance) - rest_dim) * d <? 0.0);
1377 shift /= staff_space;
1378 Real rad = Staff_symbol_referencer::line_count (rest) * staff_space / 2;
1380 /* Always move discretely by half spaces */
1381 shift = ceil (fabs (shift * 2.0)) / 2.0 * sign (shift);
1383 /* Inside staff, move by whole spaces*/
1384 if ( (rest->extent (common_y, Y_AXIS)[d] + staff_space * shift) * d
1386 || (rest->extent (common_y, Y_AXIS)[-d] + staff_space * shift) * -d
1388 shift = ceil (fabs (shift)) *sign (shift);
1390 return scm_make_real (staff_space * shift);
1394 Beam::is_knee (Grob* me)
1396 SCM k = me->get_property ("knee");
1397 if (scm_is_bool (k))
1398 return ly_scm2bool (k);
1402 for (SCM s = me->get_property ("stems"); scm_is_pair (s); s = scm_cdr (s))
1404 Direction dir = get_grob_direction (unsmob_grob (scm_car (s)));
1413 me->set_property ("knee", ly_bool2scm (knee));
1419 Beam::get_direction_beam_count (Grob *me, Direction d )
1421 Link_array<Grob>stems =
1422 extract_grob_array (me, ly_symbol2scm ("stems"));
1425 for (int i = stems.size (); i--;)
1428 Should we take invisible stems into account?
1430 if (Stem::get_direction (stems[i]) == d)
1431 bc = bc >? (Stem::beam_multiplicity (stems[i]).length () + 1);
1438 ADD_INTERFACE (Beam, "beam-interface",
1440 "The @code{thickness} property is the weight of beams, and is measured "
1443 "knee positioning-done position-callbacks "
1444 "concaveness dir-function quant-score auto-knee-gap gap "
1445 "gap-count chord-tremolo beamed-stem-shorten shorten least-squares-dy "
1446 "damping inspect-quants flag-width-function neutral-direction positions space-function "