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.
29 #include <math.h> // tanh.
32 #include "directional-element-interface.hh"
36 #include "least-squares.hh"
38 #include "output-def.hh"
40 #include "group-interface.hh"
41 #include "staff-symbol-referencer.hh"
48 #include "text-item.hh" // debug output.
49 #include "font-interface.hh" // debug output.
54 Beam::add_stem (Grob *me, Grob *s)
56 Pointer_group_interface::add_grob (me, ly_symbol2scm ("stems"), s);
58 s->add_dependency (me);
60 assert (!Stem::get_beam (s));
61 s->set_property ("beam", me->self_scm ());
63 add_bound_item (dynamic_cast<Spanner*> (me), dynamic_cast<Item*> (s));
68 Beam::get_thickness (Grob * me)
70 return robust_scm2double (me->get_property ("thickness"), 0)
71 * Staff_symbol_referencer::staff_space (me);
74 /* Return the translation between 2 adjoining beams. */
76 Beam::get_beam_translation (Grob *me)
78 SCM func = me->get_property ("space-function");
80 if (ly_c_procedure_p (func))
82 SCM s = scm_call_2 (func, me->self_scm (), scm_int2num (get_beam_count (me)));
83 return scm_to_double (s);
91 /* Maximum beam_count. */
93 Beam::get_beam_count (Grob *me)
96 for (SCM s = me->get_property ("stems"); scm_is_pair (s); s = scm_cdr (s))
98 Grob *stem = unsmob_grob (scm_car (s));
99 m = m >? (Stem::beam_multiplicity (stem).length () + 1);
106 Space return space between beams.
108 MAKE_SCHEME_CALLBACK (Beam, space_function, 2);
110 Beam::space_function (SCM smob, SCM beam_count)
112 Grob *me = unsmob_grob (smob);
114 Real staff_space = Staff_symbol_referencer::staff_space (me);
115 Real line = Staff_symbol_referencer::line_thickness (me);
116 Real thickness = get_thickness (me);
118 Real beam_translation = scm_to_int (beam_count) < 4
119 ? (2*staff_space + line - thickness) / 2.0
120 : (3*staff_space + line - thickness) / 3.0;
122 return scm_make_real (beam_translation);
126 /* After pre-processing all directions should be set.
127 Several post-processing routines (stem, slur, script) need stem/beam
129 Currenly, this means that beam has set all stem's directions.
130 [Alternatively, stems could set its own directions, according to
131 their beam, during 'final-pre-processing'.] */
132 MAKE_SCHEME_CALLBACK (Beam, before_line_breaking, 1);
134 Beam::before_line_breaking (SCM smob)
136 Grob *me = unsmob_grob (smob);
138 /* Beams with less than 2 two stems don't make much sense, but could happen
143 For a beam that only has one stem, we try to do some disappearance magic:
144 we revert the flag, and move on to The Eternal Engraving Fields. */
146 int count = visible_stem_count (me);
149 me->warning (_ ("beam has less than two visible stems"));
151 SCM stems = me->get_property ("stems");
152 if (scm_ilength (stems) == 1)
154 me->warning (_ ("removing beam with less than two stems"));
156 unsmob_grob (scm_car (stems))->set_property ("beam", SCM_EOL);
159 return SCM_UNSPECIFIED;
161 else if (scm_ilength (stems) == 0)
164 return SCM_UNSPECIFIED;
169 Direction d = get_default_dir (me);
171 consider_auto_knees (me);
172 set_stem_directions (me, d);
176 set_stem_shorten (me);
184 We want a maximal number of shared beams, but if there is choice, we
185 take the one that is closest to the end of the stem. This is for situations like
198 position_with_maximal_common_beams (SCM left_beaming, SCM right_beaming,
202 Slice lslice = int_list_to_slice (scm_cdr (left_beaming));
206 for (int i = lslice[-left_dir];
207 (i - lslice[left_dir])* left_dir <= 0 ; i+= left_dir)
210 for ( SCM s = scm_car (right_beaming); scm_is_pair (s); s = scm_cdr (s))
212 int k = - right_dir * scm_to_int (scm_car (s)) + i;
213 if (scm_c_memq (scm_int2num (k), left_beaming) != SCM_BOOL_F)
217 if (count >= best_count)
228 Beam::connect_beams (Grob *me)
230 Link_array<Grob> stems=
231 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
234 last_int.set_empty ();
235 SCM last_beaming = SCM_EOL;
236 Direction last_dir = CENTER;
237 for (int i = 0; i< stems.size (); i++)
239 Grob *this_stem = stems[i];
240 SCM this_beaming = this_stem->get_property ("beaming");
242 Direction this_dir = get_grob_direction (this_stem);
243 if (scm_is_pair (last_beaming) && scm_is_pair (this_beaming))
245 int start_point = position_with_maximal_common_beams
246 (last_beaming, this_beaming,
253 if (d == RIGHT && i == stems.size ()-1)
256 new_slice.set_empty ();
257 SCM s = index_get_cell (this_beaming, d);
258 for (; scm_is_pair (s); s = scm_cdr (s))
261 start_point - this_dir * scm_to_int (scm_car (s));
263 new_slice.add_point (new_beam_pos);
264 scm_set_car_x (s, scm_int2num (new_beam_pos));
269 while (flip (&d) != LEFT);
271 if (!new_slice.is_empty ())
272 last_int = new_slice;
276 scm_set_car_x ( this_beaming, SCM_EOL);
277 SCM s = scm_cdr (this_beaming);
278 for (; scm_is_pair (s); s = scm_cdr (s))
280 int np = - this_dir * scm_to_int (scm_car (s));
281 scm_set_car_x (s, scm_int2num (np));
282 last_int.add_point (np);
286 if (i == stems.size () -1)
288 scm_set_cdr_x (this_beaming, SCM_EOL);
291 if (scm_ilength (scm_cdr (this_beaming)) > 0)
293 last_beaming = this_beaming;
301 TODO: should not make beams per stem, but per Y-level.
303 MAKE_SCHEME_CALLBACK (Beam, print, 1);
305 Beam::print (SCM grob)
307 Spanner *me = unsmob_spanner (grob);
310 Link_array<Grob> stems=
311 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
312 Grob* xcommon = common_refpoint_of_array (stems, me, X_AXIS);
314 xcommon = me->get_bound (LEFT)->common_refpoint (xcommon, X_AXIS);
315 xcommon = me->get_bound (RIGHT)->common_refpoint (xcommon, X_AXIS);
318 if (visible_stem_count (me))
320 // ugh -> use commonx
321 x0 = first_visible_stem (me)->relative_coordinate (xcommon, X_AXIS);
322 dx = last_visible_stem (me)->relative_coordinate (xcommon, X_AXIS) - x0;
326 x0 = stems[0]->relative_coordinate (xcommon, X_AXIS);
327 dx = stems.top ()->relative_coordinate (xcommon, X_AXIS) - x0;
330 SCM posns = me->get_property ("positions");
331 Drul_array<Real> pos;
332 if (!is_number_pair (posns))
334 programming_error ("No beam posns");
335 pos = Interval (0,0);
338 pos= ly_scm2realdrul (posns);
340 scale_drul (&pos, Staff_symbol_referencer::staff_space (me));
342 Real dy = pos[RIGHT] - pos[LEFT];
343 Real slope = (dy && dx) ? dy/dx : 0;
345 Real thick = get_thickness (me);
346 Real bdy = get_beam_translation (me);
348 SCM last_beaming = SCM_EOL;
349 Real last_xposn = -1;
350 Real last_stem_width = -1 ;
352 Real gap_length = robust_scm2double (me->get_property ("gap"), 0.0);
355 Real lt = me->get_paper ()->get_dimension (ly_symbol2scm ("linethickness"));
357 for (int i = 0; i<= stems.size (); i++)
359 Grob * st = (i < stems.size ()) ? stems[i] : 0;
361 SCM this_beaming = st ? st->get_property ("beaming") : SCM_EOL;
362 Real xposn = st ? st->relative_coordinate (xcommon, X_AXIS) : 0.0;
363 Real stem_width = st ? robust_scm2double (st->get_property ("thickness"), 1.0) *lt : 0 ;
364 Direction stem_dir = st ? to_dir (st->get_property ("direction")) : CENTER;
366 We do the space left of ST, with lfliebertjes pointing to the
367 right from the left stem, and rfliebertjes pointing left from
370 SCM left = (i > 0) ? scm_cdr (last_beaming) : SCM_EOL;
371 SCM right = st ? scm_car (this_beaming) : SCM_EOL;
373 Array<int> full_beams;
374 Array<int> lfliebertjes;
375 Array<int> rfliebertjes;
378 scm_is_pair (s); s =scm_cdr (s))
380 int b = scm_to_int (scm_car (s));
381 if (scm_c_memq (scm_car (s), right) != SCM_BOOL_F)
387 lfliebertjes.push (b);
391 scm_is_pair (s); s =scm_cdr (s))
393 int b = scm_to_int (scm_car (s));
394 if (scm_c_memq (scm_car (s), left) == SCM_BOOL_F)
396 rfliebertjes.push (b);
401 how much to stick out for beams across linebreaks
403 Real break_overshoot = 3.0;
404 Real w = (i > 0 && st) ? (xposn - last_xposn) : break_overshoot;
406 Real stem_offset =0.0;
409 w += last_stem_width / 2;
410 stem_offset = -last_stem_width / 2;
417 Real blot = me->get_paper ()->get_dimension (ly_symbol2scm ("blotdiameter"));
418 Stencil whole = Lookup::beam (slope, w, thick, blot);
422 if (scm_is_number (me->get_property ("gap-count")))
424 gap_count = scm_to_int (me->get_property ("gap-count"));
425 gapped = Lookup::beam (slope, w - 2 * gap_length, thick, blot);
427 full_beams.sort (default_compare);
429 full_beams.reverse ();
433 for (int j = full_beams.size (); j--;)
440 b.translate_axis (gap_length, X_AXIS);
442 b.translate_axis (last_xposn - x0 + stem_offset, X_AXIS);
443 b.translate_axis (slope * (last_xposn - x0) + bdy * full_beams[j], Y_AXIS);
445 the_beam.add_stencil (b);
448 if (lfliebertjes.size () || rfliebertjes.size ())
454 int t = Stem::duration_log (st);
456 SCM proc = me->get_property ("flag-width-function");
457 SCM result = scm_call_1 (proc, scm_int2num (t));
458 nw_f = scm_to_double (result);
461 nw_f = break_overshoot / 2;
463 /* Half beam should be one note-width,
464 but let's make sure two half-beams never touch */
468 rw = nw_f <? ( (xposn - last_xposn) / 2);
471 TODO: 0.5 is a guess.
473 rw = xposn - me->get_bound (LEFT)->extent (xcommon, X_AXIS)[RIGHT]
477 lw = nw_f <? ( (xposn - last_xposn) / 2);
479 lw = me->get_bound (RIGHT)->relative_coordinate (xcommon, X_AXIS)
482 Stencil rhalf = Lookup::beam (slope, rw, thick, blot);
483 Stencil lhalf = Lookup::beam (slope, lw, thick, blot);
484 for (int j = lfliebertjes.size (); j--;)
487 b.translate_axis (last_xposn - x0, X_AXIS);
488 b.translate_axis (slope * (last_xposn-x0) + bdy * lfliebertjes[j], Y_AXIS);
489 the_beam.add_stencil (b);
491 for (int j = rfliebertjes.size (); j--;)
494 b.translate_axis (xposn - x0 - rw , X_AXIS);
495 b.translate_axis (slope * (xposn-x0 -rw) + bdy * rfliebertjes[j], Y_AXIS);
496 the_beam.add_stencil (b);
502 last_stem_width = stem_width;
503 last_beaming = this_beaming;
506 the_beam.translate_axis (x0 - me->relative_coordinate (xcommon, X_AXIS), X_AXIS);
507 the_beam.translate_axis (pos[LEFT], Y_AXIS);
510 SCM quant_score = me->get_property ("quant-score");
511 if (to_boolean (me->get_paper ()->lookup_variable (ly_symbol2scm ("debug-beam-quanting")))
512 && scm_is_string (quant_score))
516 This code prints the demerits for each beam. Perhaps this
517 should be switchable for those who want to twiddle with the
521 SCM properties = Font_interface::text_font_alist_chain (me);
523 Direction stem_dir = stems.size() ? to_dir (stems[0]->get_property ("direction")) : UP;
525 Stencil tm = *unsmob_stencil (Text_interface::interpret_markup
526 (me->get_paper ()->self_scm (), properties, quant_score));
527 the_beam.add_at_edge (Y_AXIS, stem_dir, tm, 1.0, 0);
531 return the_beam.smobbed_copy ();
538 Beam::get_default_dir (Grob *me)
540 Drul_array<int> total;
541 total[UP] = total[DOWN] = 0;
542 Drul_array<int> count;
543 count[UP] = count[DOWN] = 0;
546 Link_array<Grob> stems=
547 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
549 for (int i=0; i < stems.size (); i++)
552 Direction sd = get_grob_direction (s);
554 int center_distance = int (- d * Stem::head_positions (s) [-d]) >? 0;
555 int current = sd ? (1 + d * sd)/2 : center_distance;
562 } while (flip (&d) != DOWN);
564 SCM func = me->get_property ("dir-function");
565 SCM s = scm_call_2 (func,
566 scm_cons (scm_int2num (count[UP]),
567 scm_int2num (count[DOWN])),
568 scm_cons (scm_int2num (total[UP]),
569 scm_int2num (total[DOWN])));
571 if (scm_is_number (s) && scm_to_int (s))
574 /* If dir is not determined: get default */
575 return to_dir (me->get_property ("neutral-direction"));
579 /* Set all stems with non-forced direction to beam direction.
580 Urg: non-forced should become `without/with unforced' direction,
581 once stem gets cleaned-up. */
583 Beam::set_stem_directions (Grob *me, Direction d)
585 Link_array<Grob> stems
586 =Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
588 for (int i=0; i <stems.size (); i++)
592 SCM forcedir = s->get_property ("direction");
593 if (!to_dir (forcedir))
594 set_grob_direction (s, d);
599 A union of intervals in the real line.
601 Abysmal performance (quadratic) for large N, hopefully we don't have
602 that large N. In any case, this should probably be rewritten to use
607 Array<Interval> allowed_regions_;
616 allowed_regions_.clear ();
619 allowed_regions_.push (s);
622 void remove_interval (Interval rm)
624 for (int i = 0; i < allowed_regions_.size (); )
628 s.intersect (allowed_regions_[i]);
632 Interval before = allowed_regions_[i];
633 Interval after = allowed_regions_[i];
635 before[RIGHT] = s[LEFT];
636 after[LEFT] = s[RIGHT];
638 if (!before.is_empty () && before.length () > 0.0)
640 allowed_regions_.insert (before, i);
643 allowed_regions_.del (i);
644 if (!after.is_empty () && after.length () > 0.0)
646 allowed_regions_.insert (after, i);
658 Only try horizontal beams for knees. No reliable detection of
659 anything else is possible here, since we don't know funky-beaming
660 settings, or X-distances (slopes!) People that want sloped
661 knee-beams, should set the directions manually.
664 Beam::consider_auto_knees (Grob* me)
666 SCM scm = me->get_property ("auto-knee-gap");
667 if (!scm_is_number (scm))
670 Real threshold = scm_to_double (scm);
676 Link_array<Grob> stems=
677 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
679 Grob *common = common_refpoint_of_array (stems, me, Y_AXIS);
680 Real staff_space = Staff_symbol_referencer::staff_space (me);
682 Array<Interval> hps_array;
683 for (int i=0; i < stems.size (); i++)
685 Grob* stem = stems[i];
686 if (Stem::is_invisible (stem))
689 Interval hps = Stem::head_positions (stem);
690 if (!hps.is_empty ())
694 hps *= staff_space * 0.5 ;
697 We could subtract beam Y position, but this routine only
698 sets stem directions, a constant shift does not have an
702 hps += stem->relative_coordinate (common, Y_AXIS);
704 if (to_dir (stem->get_property ("direction")))
706 Direction stemdir = to_dir (stem->get_property ("direction"));
707 hps[-stemdir] = - stemdir * infinity_f;
710 hps_array.push (hps);
712 gaps.remove_interval (hps);
716 Real max_gap_len =0.0;
718 for (int i = gaps.allowed_regions_.size () -1; i >= 0 ; i--)
720 Interval gap = gaps.allowed_regions_[i];
723 the outer gaps are not knees.
725 if (isinf (gap[LEFT]) || isinf (gap[RIGHT]))
728 if (gap.length () >= max_gap_len)
730 max_gap_len = gap.length ();
735 if (max_gap_len > threshold)
738 for (int i = 0; i < stems.size (); i++)
740 Grob* stem = stems[i];
741 if (Stem::is_invisible (stem))
744 Interval hps = hps_array[j++];
747 Direction d = (hps.center () < max_gap.center ()) ?
750 stem->set_property ("direction", scm_int2num (d));
752 hps.intersect (max_gap);
753 assert (hps.is_empty () || hps.length () < 1e-6 );
760 /* Set stem's shorten property if unset.
763 take some y-position (chord/beam/nearest?) into account
764 scmify forced-fraction
766 This is done in beam because the shorten has to be uniform over the
771 Beam::set_stem_shorten (Grob *me)
774 shortening looks silly for x staff beams
779 Real forced_fraction = 1.0 * forced_stem_count (me)
780 / visible_stem_count (me);
782 int beam_count = get_beam_count (me);
784 SCM shorten_list = me->get_property ("beamed-stem-shorten");
785 if (shorten_list == SCM_EOL)
788 Real staff_space = Staff_symbol_referencer::staff_space (me);
791 robust_list_ref (beam_count -1, shorten_list);
792 Real shorten_f = scm_to_double (shorten_elt) * staff_space;
794 /* your similar cute comment here */
795 shorten_f *= forced_fraction;
798 me->set_property ("shorten", scm_make_real (shorten_f));
801 /* Call list of y-dy-callbacks, that handle setting of
805 MAKE_SCHEME_CALLBACK (Beam, after_line_breaking, 1);
807 Beam::after_line_breaking (SCM smob)
809 Grob *me = unsmob_grob (smob);
812 return SCM_UNSPECIFIED;
816 Beam::position_beam (Grob *me)
820 if (to_boolean (me->get_property ("positioning-done")))
823 me->set_property ("positioning-done", SCM_BOOL_T);
825 /* Copy to mutable list. */
826 SCM s = ly_deep_copy (me->get_property ("positions"));
827 me->set_property ("positions", s);
829 if (scm_car (s) == SCM_BOOL_F)
831 // one wonders if such genericity is necessary --hwn.
832 SCM callbacks = me->get_property ("position-callbacks");
833 for (SCM i = callbacks; scm_is_pair (i); i = scm_cdr (i))
834 scm_call_1 (scm_car (i), me->self_scm ());
837 set_stem_lengths (me);
842 set_minimum_dy (Grob *me, Real * dy)
847 If dy is smaller than the smallest quant, we
848 get absurd direction-sign penalties.
851 Real ss = Staff_symbol_referencer::staff_space (me);
852 Real thickness = Beam::get_thickness (me) / ss ;
853 Real slt = Staff_symbol_referencer::line_thickness (me) / ss;
854 Real sit = (thickness - slt) / 2;
856 Real hang = 1.0 - (thickness - slt) / 2;
858 *dy = sign (*dy) * (fabs (*dy)
860 (sit <? inter <? hang));
865 Compute a first approximation to the beam slope.
867 MAKE_SCHEME_CALLBACK (Beam, least_squares, 1);
869 Beam::least_squares (SCM smob)
871 Grob *me = unsmob_grob (smob);
873 int count = visible_stem_count (me);
878 me->set_property ("positions", ly_interval2scm (pos));
879 return SCM_UNSPECIFIED;
883 Array<Real> x_posns ;
884 Link_array<Grob> stems=
885 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
886 Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
887 Grob *commony = common_refpoint_of_array (stems, me, Y_AXIS);
889 Real my_y = me->relative_coordinate (commony, Y_AXIS);
891 Grob *fvs = first_visible_stem (me);
892 Grob *lvs = last_visible_stem (me);
894 Interval ideal (Stem::get_stem_info (fvs).ideal_y_
895 + fvs->relative_coordinate (commony, Y_AXIS) -my_y,
896 Stem::get_stem_info (lvs).ideal_y_
897 + lvs->relative_coordinate (commony, Y_AXIS) - my_y);
899 Real x0 = first_visible_stem (me)->relative_coordinate (commonx, X_AXIS);
900 for (int i=0; i < stems.size (); i++)
904 Real x = s->relative_coordinate (commonx, X_AXIS) - x0;
907 Real dx = last_visible_stem (me)->relative_coordinate (commonx, X_AXIS) - x0;
916 Interval chord (Stem::chord_start_y (first_visible_stem (me)),
917 Stem::chord_start_y (last_visible_stem (me)));
919 /* Simple beams (2 stems) on middle line should be allowed to be
922 However, if both stems reach middle line,
923 ideal[LEFT] == ideal[RIGHT] and ideal.delta () == 0.
925 For that case, we apply artificial slope */
926 if (!ideal[LEFT] && chord.delta () && count == 2)
929 Direction d = (Direction) (sign (chord.delta ()) * UP);
930 pos[d] = get_thickness (me) / 2;
939 For broken beams this doesn't work well. In this case, the
940 slope esp. of the first part of a broken beam should predict
941 where the second part goes.
943 me->set_property ("least-squares-dy",
944 scm_make_real (pos[RIGHT] - pos[LEFT]));
948 Array<Offset> ideals;
949 for (int i=0; i < stems.size (); i++)
952 if (Stem::is_invisible (s))
954 ideals.push (Offset (x_posns[i],
955 Stem::get_stem_info (s).ideal_y_
956 + s->relative_coordinate (commony, Y_AXIS)
960 minimise_least_squares (&slope, &y, ideals);
964 set_minimum_dy (me,&dy);
965 me->set_property ("least-squares-dy", scm_make_real (dy));
966 pos = Interval (y, (y+dy));
970 "position" is relative to the staff.
972 scale_drul (&pos, 1/ Staff_symbol_referencer::staff_space (me));
974 me->set_property ("positions", ly_interval2scm (pos));
976 return SCM_UNSPECIFIED;
981 We can't combine with previous function, since check concave and
982 slope damping comes first.
984 TODO: we should use the concaveness to control the amount of damping
988 MAKE_SCHEME_CALLBACK (Beam, shift_region_to_valid, 1);
990 Beam::shift_region_to_valid (SCM grob)
992 Grob *me = unsmob_grob (grob);
996 Array<Real> x_posns ;
997 Link_array<Grob> stems=
998 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
999 Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
1000 Grob *commony = common_refpoint_of_array (stems, me, Y_AXIS);
1002 Grob *fvs = first_visible_stem (me);
1005 return SCM_UNSPECIFIED;
1007 Real x0 =fvs->relative_coordinate (commonx, X_AXIS);
1008 for (int i=0; i < stems.size (); i++)
1012 Real x = s->relative_coordinate (commonx, X_AXIS) - x0;
1016 Grob *lvs = last_visible_stem (me);
1018 return SCM_UNSPECIFIED;
1020 Real dx = lvs->relative_coordinate (commonx, X_AXIS) - x0;
1022 Drul_array<Real> pos = ly_scm2interval ( me->get_property ("positions"));
1024 scale_drul (&pos, Staff_symbol_referencer::staff_space (me));
1026 Real dy = pos[RIGHT] - pos[LEFT];
1032 Shift the positions so that we have a chance of finding good
1033 quants (i.e. no short stem failures.)
1035 Interval feasible_left_point;
1036 feasible_left_point.set_full ();
1037 for (int i=0; i < stems.size (); i++)
1040 if (Stem::is_invisible (s))
1043 Direction d = Stem::get_direction (s);
1046 Stem::get_stem_info (s).shortest_y_
1047 - slope * x_posns [i];
1050 left_y is now relative to the stem S. We want relative to
1051 ourselves, so translate:
1054 + s->relative_coordinate (commony, Y_AXIS)
1055 - me->relative_coordinate (commony, Y_AXIS);
1061 feasible_left_point.intersect (flp);
1064 if (feasible_left_point.is_empty ())
1065 warning (_ ("no viable initial configuration found: may not find good beam slope"));
1066 else if (!feasible_left_point.contains (y))
1068 if (isinf (feasible_left_point[DOWN]))
1069 y = feasible_left_point[UP] - REGION_SIZE;
1070 else if (isinf (feasible_left_point[UP]))
1071 y = feasible_left_point[DOWN]+ REGION_SIZE;
1073 y = feasible_left_point.center ();
1076 pos = Drul_array<Real> (y, (y+dy));
1077 scale_drul (&pos, 1/ Staff_symbol_referencer::staff_space (me));
1079 me->set_property ("positions", ly_interval2scm (pos));
1080 return SCM_UNSPECIFIED;
1083 /* This neat trick is by Werner Lemberg,
1084 damped = tanh (slope)
1085 corresponds with some tables in [Wanske] CHECKME */
1086 MAKE_SCHEME_CALLBACK (Beam, slope_damping, 1);
1088 Beam::slope_damping (SCM smob)
1090 Grob *me = unsmob_grob (smob);
1092 if (visible_stem_count (me) <= 1)
1093 return SCM_UNSPECIFIED;
1095 SCM s = me->get_property ("damping");
1096 Real damping = scm_to_double (s);
1100 Drul_array<Real> pos = ly_scm2interval (me->get_property ("positions"));
1101 scale_drul (&pos, Staff_symbol_referencer::staff_space (me));
1103 Real dy = pos[RIGHT] - pos[LEFT];
1105 Grob *fvs = first_visible_stem (me);
1106 Grob *lvs = last_visible_stem (me);
1108 Grob *commonx = fvs->common_refpoint (lvs, X_AXIS);
1111 Real dx = last_visible_stem (me)->relative_coordinate (commonx, X_AXIS)
1112 - first_visible_stem (me)->relative_coordinate (commonx, X_AXIS);
1114 Real slope = dy && dx ? dy/dx : 0;
1116 Real concaveness = robust_scm2double (me->get_property ("concaveness"), 0.0);
1118 slope = 0.6 * tanh (slope) / (damping + concaveness);
1120 Real damped_dy = slope * dx;
1122 set_minimum_dy (me, &damped_dy);
1124 pos[LEFT] += (dy - damped_dy) / 2;
1125 pos[RIGHT] -= (dy - damped_dy) / 2;
1127 scale_drul (&pos, 1/Staff_symbol_referencer::staff_space (me));
1129 me->set_property ("positions", ly_interval2scm (pos));
1131 return SCM_UNSPECIFIED;
1135 Report slice containing the numbers that are both in (car BEAMING)
1139 where_are_the_whole_beams (SCM beaming)
1143 for ( SCM s = scm_car (beaming); scm_is_pair (s) ; s = scm_cdr (s))
1145 if (scm_c_memq (scm_car (s), scm_cdr (beaming)) != SCM_BOOL_F)
1147 l.add_point (scm_to_int (scm_car (s)));
1153 /* Return the Y position of the stem-end, given the Y-left, Y-right
1154 in POS for stem S. This Y position is relative to S. */
1156 Beam::calc_stem_y (Grob *me, Grob* s, Grob ** common,
1158 Drul_array<Real> pos, bool french)
1160 Real beam_translation = get_beam_translation (me);
1163 Real r = s->relative_coordinate (common[X_AXIS], X_AXIS) - xl;
1164 Real dy = pos[RIGHT] - pos[LEFT];
1166 Real stem_y_beam0 = (dy && dx
1171 Direction my_dir = get_grob_direction (s);
1172 SCM beaming = s->get_property ("beaming");
1174 Real stem_y = stem_y_beam0;
1177 Slice bm = where_are_the_whole_beams (beaming);
1178 if (!bm.is_empty ())
1179 stem_y += beam_translation * bm[-my_dir];
1183 Slice bm = Stem::beam_multiplicity (s);
1184 if (!bm.is_empty ())
1185 stem_y +=bm[my_dir] * beam_translation;
1188 Real id = me->relative_coordinate (common[Y_AXIS], Y_AXIS)
1189 - s->relative_coordinate (common[Y_AXIS], Y_AXIS);
1195 Hmm. At this time, beam position and slope are determined. Maybe,
1196 stem directions and length should set to relative to the chord's
1197 position of the beam. */
1199 Beam::set_stem_lengths (Grob *me)
1201 Link_array<Grob> stems=
1202 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
1208 for (int a = 2; a--;)
1209 common[a] = common_refpoint_of_array (stems, me, Axis (a));
1211 Drul_array<Real> pos = ly_scm2realdrul (me->get_property ("positions"));
1212 Real staff_space = Staff_symbol_referencer::staff_space (me);
1213 scale_drul (&pos, staff_space);
1217 if (scm_is_number (me->get_property ("gap-count"))
1218 &&scm_to_int (me->get_property ("gap-count")))
1221 thick = get_thickness (me);
1224 // ugh -> use commonx
1225 Grob * fvs = first_visible_stem (me);
1226 Grob *lvs = last_visible_stem (me);
1228 Real xl = fvs ? fvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
1229 Real xr = lvs ? lvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
1231 for (int i=0; i < stems.size (); i++)
1234 if (Stem::is_invisible (s))
1237 bool french = to_boolean (s->get_property ("french-beaming"));
1238 Real stem_y = calc_stem_y (me, s, common,
1240 pos, french && s != lvs && s!= fvs);
1243 Make the stems go up to the end of the beam. This doesn't matter
1244 for normal beams, but for tremolo beams it looks silly otherwise.
1247 stem_y += thick * 0.5 * get_grob_direction (s);
1249 Stem::set_stemend (s, 2* stem_y / staff_space);
1254 Beam::set_beaming (Grob *me, Beaming_info_list *beaming)
1256 Link_array<Grob> stems=
1257 Pointer_group_interface__extract_grobs (me, (Grob *)0, "stems");
1260 for (int i=0; i < stems.size (); i++)
1263 Don't overwrite user settings.
1268 /* Don't set beaming for outside of outer stems */
1269 if ( (d == LEFT && i == 0)
1270 || (d == RIGHT && i == stems.size () -1))
1273 Grob *st = stems[i];
1274 SCM beaming_prop = st->get_property ("beaming");
1275 if (beaming_prop == SCM_EOL ||
1276 index_get_cell (beaming_prop, d) == SCM_EOL)
1278 int b = beaming->infos_.elem (i).beams_i_drul_[d];
1280 && i < stems.size () -1
1281 && Stem::is_invisible (st))
1282 b = b <? beaming->infos_.elem (i).beams_i_drul_[-d];
1284 Stem::set_beaming (st, b, d);
1287 while (flip (&d) != LEFT);
1292 Beam::forced_stem_count (Grob *me)
1294 Link_array<Grob>stems =
1295 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1297 for (int i=0; i < stems.size (); i++)
1301 if (Stem::is_invisible (s))
1304 /* I can imagine counting those boundaries as a half forced stem,
1305 but let's count them full for now. */
1306 if (abs (Stem::chord_start_y (s)) > 0.1
1307 && (Stem::get_direction (s) != Stem::get_default_dir (s)))
1317 Beam::visible_stem_count (Grob *me)
1319 Link_array<Grob>stems =
1320 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1322 for (int i = stems.size (); i--;)
1324 if (!Stem::is_invisible (stems[i]))
1331 Beam::first_visible_stem (Grob *me)
1333 Link_array<Grob>stems =
1334 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1336 for (int i = 0; i < stems.size (); i++)
1338 if (!Stem::is_invisible (stems[i]))
1345 Beam::last_visible_stem (Grob *me)
1347 Link_array<Grob>stems =
1348 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1349 for (int i = stems.size (); i--;)
1351 if (!Stem::is_invisible (stems[i]))
1361 handle rest under beam (do_post: beams are calculated now)
1362 what about combination of collisions and rest under beam.
1366 rest -> stem -> beam -> interpolate_y_position ()
1368 MAKE_SCHEME_CALLBACK (Beam, rest_collision_callback, 2);
1370 Beam::rest_collision_callback (SCM element_smob, SCM axis)
1372 Grob *rest = unsmob_grob (element_smob);
1373 Axis a = (Axis) scm_to_int (axis);
1375 if (scm_is_number (rest->get_property ("staff-position")))
1376 return scm_int2num (0);
1378 assert (a == Y_AXIS);
1380 Grob *st = unsmob_grob (rest->get_property ("stem"));
1383 return scm_make_real (0.0);
1384 Grob *beam = unsmob_grob (stem->get_property ("beam"));
1386 || !Beam::has_interface (beam)
1387 || !Beam::visible_stem_count (beam))
1388 return scm_make_real (0.0);
1390 Drul_array<Real> pos (0, 0);
1391 SCM s = beam->get_property ("positions");
1392 if (scm_is_pair (s) && scm_is_number (scm_car (s)))
1393 pos = ly_scm2interval (s);
1394 Real staff_space = Staff_symbol_referencer::staff_space (rest);
1396 scale_drul (&pos, staff_space);
1399 Real dy = pos[RIGHT] - pos[LEFT];
1401 // ugh -> use commonx
1402 Real x0 = first_visible_stem (beam)->relative_coordinate (0, X_AXIS);
1403 Real dx = last_visible_stem (beam)->relative_coordinate (0, X_AXIS) - x0;
1404 Real slope = dy && dx ? dy/dx : 0;
1406 Direction d = Stem::get_direction (stem);
1407 Real stem_y = pos[LEFT] + (stem->relative_coordinate (0, X_AXIS) - x0) * slope;
1409 Real beam_translation = get_beam_translation (beam);
1410 Real beam_thickness = Beam::get_thickness (beam);
1412 int beam_count = get_direction_beam_count (beam, d);
1413 Real height_of_my_beams = beam_thickness / 2
1414 + (beam_count - 1) * beam_translation;
1415 Real beam_y = stem_y - d * height_of_my_beams;
1417 Grob *common_y = rest->common_refpoint (beam, Y_AXIS);
1419 Real rest_dim = rest->extent (common_y, Y_AXIS)[d];
1420 Real minimum_distance =
1421 staff_space * robust_scm2double (rest->get_property ("minimum-distance"), 0.0);
1423 Real shift = d * ( ((beam_y - d * minimum_distance) - rest_dim) * d <? 0.0);
1425 shift /= staff_space;
1426 Real rad = Staff_symbol_referencer::line_count (rest) * staff_space / 2;
1428 /* Always move discretely by half spaces */
1429 shift = ceil (fabs (shift * 2.0)) / 2.0 * sign (shift);
1431 /* Inside staff, move by whole spaces*/
1432 if ( (rest->extent (common_y, Y_AXIS)[d] + staff_space * shift) * d
1434 || (rest->extent (common_y, Y_AXIS)[-d] + staff_space * shift) * -d
1436 shift = ceil (fabs (shift)) *sign (shift);
1438 return scm_make_real (staff_space * shift);
1442 Beam::is_knee (Grob* me)
1444 SCM k = me->get_property ("knee");
1445 if (scm_is_bool (k))
1446 return ly_scm2bool (k);
1450 for (SCM s = me->get_property ("stems"); scm_is_pair (s); s = scm_cdr (s))
1452 Direction dir = get_grob_direction (unsmob_grob (scm_car (s)));
1461 me->set_property ("knee", ly_bool2scm (knee));
1467 Beam::get_direction_beam_count (Grob *me, Direction d )
1469 Link_array<Grob>stems =
1470 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1473 for (int i = stems.size (); i--;)
1476 Should we take invisible stems into account?
1478 if (Stem::get_direction (stems[i]) == d)
1479 bc = bc >? (Stem::beam_multiplicity (stems[i]).length () + 1);
1486 ADD_INTERFACE (Beam, "beam-interface",
1488 "The @code{thickness} property is the weight of beams, and is measured "
1491 "knee positioning-done position-callbacks "
1492 "concaveness dir-function quant-score auto-knee-gap gap "
1493 "gap-count chord-tremolo beamed-stem-shorten shorten least-squares-dy "
1494 "damping inspect-quants flag-width-function neutral-direction positions space-function "