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"); ly_c_pair_p (s); s = ly_cdr (s))
98 Grob *stem = unsmob_grob (ly_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 (ly_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 (ly_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 = ly_car (right_beaming); ly_c_pair_p (s); s = ly_cdr (s))
212 int k = - right_dir * scm_to_int (ly_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 (ly_c_pair_p (last_beaming) && ly_c_pair_p (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 (; ly_c_pair_p (s); s = ly_cdr (s))
261 start_point - this_dir * scm_to_int (ly_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 = ly_cdr (this_beaming);
278 for (; ly_c_pair_p (s); s = ly_cdr (s))
280 int np = - this_dir * scm_to_int (ly_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 (ly_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) ? ly_cdr (last_beaming) : SCM_EOL;
371 SCM right = st ? ly_car (this_beaming) : SCM_EOL;
373 Array<int> full_beams;
374 Array<int> lfliebertjes;
375 Array<int> rfliebertjes;
378 ly_c_pair_p (s); s =ly_cdr (s))
380 int b = scm_to_int (ly_car (s));
381 if (scm_c_memq (ly_car (s), right) != SCM_BOOL_F)
387 lfliebertjes.push (b);
391 ly_c_pair_p (s); s =ly_cdr (s))
393 int b = scm_to_int (ly_car (s));
394 if (scm_c_memq (ly_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_item::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)
818 if (to_boolean (me->get_property ("positioning-done")))
821 me->set_property ("positioning-done", SCM_BOOL_T);
823 /* Copy to mutable list. */
824 SCM s = ly_deep_copy (me->get_property ("positions"));
825 me->set_property ("positions", s);
827 if (ly_car (s) == SCM_BOOL_F)
829 // one wonders if such genericity is necessary --hwn.
830 SCM callbacks = me->get_property ("position-callbacks");
831 for (SCM i = callbacks; ly_c_pair_p (i); i = ly_cdr (i))
832 scm_call_1 (ly_car (i), me->self_scm ());
835 set_stem_lengths (me);
840 set_minimum_dy (Grob *me, Real * dy)
845 If dy is smaller than the smallest quant, we
846 get absurd direction-sign penalties.
849 Real ss = Staff_symbol_referencer::staff_space (me);
850 Real thickness = Beam::get_thickness (me) / ss ;
851 Real slt = Staff_symbol_referencer::line_thickness (me) / ss;
852 Real sit = (thickness - slt) / 2;
854 Real hang = 1.0 - (thickness - slt) / 2;
856 *dy = sign (*dy) * (fabs (*dy)
858 (sit <? inter <? hang));
863 Compute a first approximation to the beam slope.
865 MAKE_SCHEME_CALLBACK (Beam, least_squares, 1);
867 Beam::least_squares (SCM smob)
869 Grob *me = unsmob_grob (smob);
871 int count = visible_stem_count (me);
876 me->set_property ("positions", ly_interval2scm (pos));
877 return SCM_UNSPECIFIED;
881 Array<Real> x_posns ;
882 Link_array<Grob> stems=
883 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
884 Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
885 Grob *commony = common_refpoint_of_array (stems, me, Y_AXIS);
887 Real my_y = me->relative_coordinate (commony, Y_AXIS);
889 Grob *fvs = first_visible_stem (me);
890 Grob *lvs = last_visible_stem (me);
892 Interval ideal (Stem::get_stem_info (fvs).ideal_y_
893 + fvs->relative_coordinate (commony, Y_AXIS) -my_y,
894 Stem::get_stem_info (lvs).ideal_y_
895 + lvs->relative_coordinate (commony, Y_AXIS) - my_y);
897 Real x0 = first_visible_stem (me)->relative_coordinate (commonx, X_AXIS);
898 for (int i=0; i < stems.size (); i++)
902 Real x = s->relative_coordinate (commonx, X_AXIS) - x0;
905 Real dx = last_visible_stem (me)->relative_coordinate (commonx, X_AXIS) - x0;
914 Interval chord (Stem::chord_start_y (first_visible_stem (me)),
915 Stem::chord_start_y (last_visible_stem (me)));
917 /* Simple beams (2 stems) on middle line should be allowed to be
920 However, if both stems reach middle line,
921 ideal[LEFT] == ideal[RIGHT] and ideal.delta () == 0.
923 For that case, we apply artificial slope */
924 if (!ideal[LEFT] && chord.delta () && count == 2)
927 Direction d = (Direction) (sign (chord.delta ()) * UP);
928 pos[d] = get_thickness (me) / 2;
937 For broken beams this doesn't work well. In this case, the
938 slope esp. of the first part of a broken beam should predict
939 where the second part goes.
941 me->set_property ("least-squares-dy",
942 scm_make_real (pos[RIGHT] - pos[LEFT]));
946 Array<Offset> ideals;
947 for (int i=0; i < stems.size (); i++)
950 if (Stem::is_invisible (s))
952 ideals.push (Offset (x_posns[i],
953 Stem::get_stem_info (s).ideal_y_
954 + s->relative_coordinate (commony, Y_AXIS)
958 minimise_least_squares (&slope, &y, ideals);
962 set_minimum_dy (me,&dy);
963 me->set_property ("least-squares-dy", scm_make_real (dy));
964 pos = Interval (y, (y+dy));
968 "position" is relative to the staff.
970 scale_drul (&pos, 1/ Staff_symbol_referencer::staff_space (me));
972 me->set_property ("positions", ly_interval2scm (pos));
974 return SCM_UNSPECIFIED;
979 We can't combine with previous function, since check concave and
980 slope damping comes first.
982 TODO: we should use the concaveness to control the amount of damping
986 MAKE_SCHEME_CALLBACK (Beam, shift_region_to_valid, 1);
988 Beam::shift_region_to_valid (SCM grob)
990 Grob *me = unsmob_grob (grob);
994 Array<Real> x_posns ;
995 Link_array<Grob> stems=
996 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
997 Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
998 Grob *commony = common_refpoint_of_array (stems, me, Y_AXIS);
1000 Grob *fvs = first_visible_stem (me);
1003 return SCM_UNSPECIFIED;
1005 Real x0 =fvs->relative_coordinate (commonx, X_AXIS);
1006 for (int i=0; i < stems.size (); i++)
1010 Real x = s->relative_coordinate (commonx, X_AXIS) - x0;
1014 Grob *lvs = last_visible_stem (me);
1016 return SCM_UNSPECIFIED;
1018 Real dx = lvs->relative_coordinate (commonx, X_AXIS) - x0;
1020 Drul_array<Real> pos = ly_scm2interval ( me->get_property ("positions"));
1022 scale_drul (&pos, Staff_symbol_referencer::staff_space (me));
1024 Real dy = pos[RIGHT] - pos[LEFT];
1030 Shift the positions so that we have a chance of finding good
1031 quants (i.e. no short stem failures.)
1033 Interval feasible_left_point;
1034 feasible_left_point.set_full ();
1035 for (int i=0; i < stems.size (); i++)
1038 if (Stem::is_invisible (s))
1041 Direction d = Stem::get_direction (s);
1044 Stem::get_stem_info (s).shortest_y_
1045 - slope * x_posns [i];
1048 left_y is now relative to the stem S. We want relative to
1049 ourselves, so translate:
1052 + s->relative_coordinate (commony, Y_AXIS)
1053 - me->relative_coordinate (commony, Y_AXIS);
1059 feasible_left_point.intersect (flp);
1062 if (feasible_left_point.is_empty ())
1063 warning (_ ("no viable initial configuration found: may not find good beam slope"));
1064 else if (!feasible_left_point.contains (y))
1066 if (isinf (feasible_left_point[DOWN]))
1067 y = feasible_left_point[UP] - REGION_SIZE;
1068 else if (isinf (feasible_left_point[UP]))
1069 y = feasible_left_point[DOWN]+ REGION_SIZE;
1071 y = feasible_left_point.center ();
1074 pos = Drul_array<Real> (y, (y+dy));
1075 scale_drul (&pos, 1/ Staff_symbol_referencer::staff_space (me));
1077 me->set_property ("positions", ly_interval2scm (pos));
1078 return SCM_UNSPECIFIED;
1081 /* This neat trick is by Werner Lemberg,
1082 damped = tanh (slope)
1083 corresponds with some tables in [Wanske] CHECKME */
1084 MAKE_SCHEME_CALLBACK (Beam, slope_damping, 1);
1086 Beam::slope_damping (SCM smob)
1088 Grob *me = unsmob_grob (smob);
1090 if (visible_stem_count (me) <= 1)
1091 return SCM_UNSPECIFIED;
1093 SCM s = me->get_property ("damping");
1094 Real damping = scm_to_double (s);
1098 Drul_array<Real> pos = ly_scm2interval (me->get_property ("positions"));
1099 scale_drul (&pos, Staff_symbol_referencer::staff_space (me));
1101 Real dy = pos[RIGHT] - pos[LEFT];
1103 Grob *fvs = first_visible_stem (me);
1104 Grob *lvs = last_visible_stem (me);
1106 Grob *commonx = fvs->common_refpoint (lvs, X_AXIS);
1109 Real dx = last_visible_stem (me)->relative_coordinate (commonx, X_AXIS)
1110 - first_visible_stem (me)->relative_coordinate (commonx, X_AXIS);
1112 Real slope = dy && dx ? dy/dx : 0;
1114 Real concaveness = robust_scm2double (me->get_property ("concaveness"), 0.0);
1116 slope = 0.6 * tanh (slope) / (damping + concaveness);
1118 Real damped_dy = slope * dx;
1120 set_minimum_dy (me, &damped_dy);
1122 pos[LEFT] += (dy - damped_dy) / 2;
1123 pos[RIGHT] -= (dy - damped_dy) / 2;
1125 scale_drul (&pos, 1/Staff_symbol_referencer::staff_space (me));
1127 me->set_property ("positions", ly_interval2scm (pos));
1129 return SCM_UNSPECIFIED;
1133 Report slice containing the numbers that are both in (car BEAMING)
1137 where_are_the_whole_beams (SCM beaming)
1141 for ( SCM s = ly_car (beaming); ly_c_pair_p (s) ; s = ly_cdr (s))
1143 if (scm_c_memq (ly_car (s), ly_cdr (beaming)) != SCM_BOOL_F)
1145 l.add_point (scm_to_int (ly_car (s)));
1151 /* Return the Y position of the stem-end, given the Y-left, Y-right
1152 in POS for stem S. This Y position is relative to S. */
1154 Beam::calc_stem_y (Grob *me, Grob* s, Grob ** common,
1156 Drul_array<Real> pos, bool french)
1158 Real beam_translation = get_beam_translation (me);
1161 Real r = s->relative_coordinate (common[X_AXIS], X_AXIS) - xl;
1162 Real dy = pos[RIGHT] - pos[LEFT];
1164 Real stem_y_beam0 = (dy && dx
1169 Direction my_dir = get_grob_direction (s);
1170 SCM beaming = s->get_property ("beaming");
1172 Real stem_y = stem_y_beam0;
1175 Slice bm = where_are_the_whole_beams (beaming);
1176 if (!bm.is_empty ())
1177 stem_y += beam_translation * bm[-my_dir];
1181 Slice bm = Stem::beam_multiplicity (s);
1182 if (!bm.is_empty ())
1183 stem_y +=bm[my_dir] * beam_translation;
1186 Real id = me->relative_coordinate (common[Y_AXIS], Y_AXIS)
1187 - s->relative_coordinate (common[Y_AXIS], Y_AXIS);
1193 Hmm. At this time, beam position and slope are determined. Maybe,
1194 stem directions and length should set to relative to the chord's
1195 position of the beam. */
1197 Beam::set_stem_lengths (Grob *me)
1199 Link_array<Grob> stems=
1200 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
1206 for (int a = 2; a--;)
1207 common[a] = common_refpoint_of_array (stems, me, Axis (a));
1209 Drul_array<Real> pos = ly_scm2realdrul (me->get_property ("positions"));
1210 Real staff_space = Staff_symbol_referencer::staff_space (me);
1211 scale_drul (&pos, staff_space);
1215 if (scm_is_number (me->get_property ("gap-count"))
1216 &&scm_to_int (me->get_property ("gap-count")))
1219 thick = get_thickness (me);
1222 // ugh -> use commonx
1223 Grob * fvs = first_visible_stem (me);
1224 Grob *lvs = last_visible_stem (me);
1226 Real xl = fvs ? fvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
1227 Real xr = lvs ? lvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
1229 for (int i=0; i < stems.size (); i++)
1232 if (Stem::is_invisible (s))
1235 bool french = to_boolean (s->get_property ("french-beaming"));
1236 Real stem_y = calc_stem_y (me, s, common,
1238 pos, french && s != lvs && s!= fvs);
1241 Make the stems go up to the end of the beam. This doesn't matter
1242 for normal beams, but for tremolo beams it looks silly otherwise.
1245 stem_y += thick * 0.5 * get_grob_direction (s);
1247 Stem::set_stemend (s, 2* stem_y / staff_space);
1252 Beam::set_beaming (Grob *me, Beaming_info_list *beaming)
1254 Link_array<Grob> stems=
1255 Pointer_group_interface__extract_grobs (me, (Grob *)0, "stems");
1258 for (int i=0; i < stems.size (); i++)
1261 Don't overwrite user settings.
1266 /* Don't set beaming for outside of outer stems */
1267 if ( (d == LEFT && i == 0)
1268 || (d == RIGHT && i == stems.size () -1))
1271 Grob *st = stems[i];
1272 SCM beaming_prop = st->get_property ("beaming");
1273 if (beaming_prop == SCM_EOL ||
1274 index_get_cell (beaming_prop, d) == SCM_EOL)
1276 int b = beaming->infos_.elem (i).beams_i_drul_[d];
1278 && i < stems.size () -1
1279 && Stem::is_invisible (st))
1280 b = b <? beaming->infos_.elem (i).beams_i_drul_[-d];
1282 Stem::set_beaming (st, b, d);
1285 while (flip (&d) != LEFT);
1290 Beam::forced_stem_count (Grob *me)
1292 Link_array<Grob>stems =
1293 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1295 for (int i=0; i < stems.size (); i++)
1299 if (Stem::is_invisible (s))
1302 /* I can imagine counting those boundaries as a half forced stem,
1303 but let's count them full for now. */
1304 if (abs (Stem::chord_start_y (s)) > 0.1
1305 && (Stem::get_direction (s) != Stem::get_default_dir (s)))
1315 Beam::visible_stem_count (Grob *me)
1317 Link_array<Grob>stems =
1318 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1320 for (int i = stems.size (); i--;)
1322 if (!Stem::is_invisible (stems[i]))
1329 Beam::first_visible_stem (Grob *me)
1331 Link_array<Grob>stems =
1332 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1334 for (int i = 0; i < stems.size (); i++)
1336 if (!Stem::is_invisible (stems[i]))
1343 Beam::last_visible_stem (Grob *me)
1345 Link_array<Grob>stems =
1346 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1347 for (int i = stems.size (); i--;)
1349 if (!Stem::is_invisible (stems[i]))
1359 handle rest under beam (do_post: beams are calculated now)
1360 what about combination of collisions and rest under beam.
1364 rest -> stem -> beam -> interpolate_y_position ()
1366 MAKE_SCHEME_CALLBACK (Beam, rest_collision_callback, 2);
1368 Beam::rest_collision_callback (SCM element_smob, SCM axis)
1370 Grob *rest = unsmob_grob (element_smob);
1371 Axis a = (Axis) scm_to_int (axis);
1373 if (scm_is_number (rest->get_property ("staff-position")))
1374 return scm_int2num (0);
1376 assert (a == Y_AXIS);
1378 Grob *st = unsmob_grob (rest->get_property ("stem"));
1381 return scm_make_real (0.0);
1382 Grob *beam = unsmob_grob (stem->get_property ("beam"));
1384 || !Beam::has_interface (beam)
1385 || !Beam::visible_stem_count (beam))
1386 return scm_make_real (0.0);
1388 Drul_array<Real> pos (0, 0);
1389 SCM s = beam->get_property ("positions");
1390 if (ly_c_pair_p (s) && scm_is_number (ly_car (s)))
1391 pos = ly_scm2interval (s);
1392 Real staff_space = Staff_symbol_referencer::staff_space (rest);
1394 scale_drul (&pos, staff_space);
1397 Real dy = pos[RIGHT] - pos[LEFT];
1399 // ugh -> use commonx
1400 Real x0 = first_visible_stem (beam)->relative_coordinate (0, X_AXIS);
1401 Real dx = last_visible_stem (beam)->relative_coordinate (0, X_AXIS) - x0;
1402 Real slope = dy && dx ? dy/dx : 0;
1404 Direction d = Stem::get_direction (stem);
1405 Real stem_y = pos[LEFT] + (stem->relative_coordinate (0, X_AXIS) - x0) * slope;
1407 Real beam_translation = get_beam_translation (beam);
1408 Real beam_thickness = Beam::get_thickness (beam);
1410 int beam_count = get_direction_beam_count (beam, d);
1411 Real height_of_my_beams = beam_thickness / 2
1412 + (beam_count - 1) * beam_translation;
1413 Real beam_y = stem_y - d * height_of_my_beams;
1415 Grob *common_y = rest->common_refpoint (beam, Y_AXIS);
1417 Real rest_dim = rest->extent (common_y, Y_AXIS)[d];
1418 Real minimum_distance =
1419 staff_space * robust_scm2double (rest->get_property ("minimum-distance"), 0.0);
1421 Real shift = d * ( ((beam_y - d * minimum_distance) - rest_dim) * d <? 0.0);
1423 shift /= staff_space;
1424 Real rad = Staff_symbol_referencer::line_count (rest) * staff_space / 2;
1426 /* Always move discretely by half spaces */
1427 shift = ceil (fabs (shift * 2.0)) / 2.0 * sign (shift);
1429 /* Inside staff, move by whole spaces*/
1430 if ( (rest->extent (common_y, Y_AXIS)[d] + staff_space * shift) * d
1432 || (rest->extent (common_y, Y_AXIS)[-d] + staff_space * shift) * -d
1434 shift = ceil (fabs (shift)) *sign (shift);
1436 return scm_make_real (staff_space * shift);
1440 Beam::is_knee (Grob* me)
1442 SCM k = me->get_property ("knee");
1443 if (scm_is_bool (k))
1444 return ly_scm2bool (k);
1448 for (SCM s = me->get_property ("stems"); ly_c_pair_p (s); s = ly_cdr (s))
1450 Direction dir = get_grob_direction (unsmob_grob (ly_car (s)));
1459 me->set_property ("knee", ly_bool2scm (knee));
1465 Beam::get_direction_beam_count (Grob *me, Direction d )
1467 Link_array<Grob>stems =
1468 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1471 for (int i = stems.size (); i--;)
1474 Should we take invisible stems into account?
1476 if (Stem::get_direction (stems[i]) == d)
1477 bc = bc >? (Stem::beam_multiplicity (stems[i]).length () + 1);
1484 ADD_INTERFACE (Beam, "beam-interface",
1486 "The @code{thickness} property is the weight of beams, and is measured "
1489 "knee positioning-done position-callbacks "
1490 "concaveness dir-function quant-score auto-knee-gap gap "
1491 "gap-count chord-tremolo beamed-stem-shorten shorten least-squares-dy "
1492 "damping inspect-quants flag-width-function neutral-direction positions space-function "