2 beam.cc -- implement Beam
4 source file of the GNU LilyPond music typesetter
6 (c) 1997--2003 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7 Jan Nieuwenhuizen <janneke@gnu.org>
13 * Use Number_pair i.s.o Interval to represent (yl, yr).
15 - Determine auto knees based on positions if it's set by the user.
21 - Stems run to the Y-center of the beam.
23 - beam_translation is the offset between Y centers of the beam.
28 #include <math.h> // tanh.
30 #include "molecule.hh"
31 #include "directional-element-interface.hh"
35 #include "least-squares.hh"
37 #include "paper-def.hh"
39 #include "group-interface.hh"
40 #include "staff-symbol-referencer.hh"
46 #define DEBUG_QUANTING 0
50 #include "text-item.hh" // debug output.
51 #include "font-interface.hh" // debug output.
56 Beam::add_stem (Grob *me, Grob *s)
58 Pointer_group_interface::add_grob (me, ly_symbol2scm ("stems"), s);
60 s->add_dependency (me);
62 assert (!Stem::get_beam (s));
63 s->set_grob_property ("beam", me->self_scm ());
65 add_bound_item (dynamic_cast<Spanner*> (me), dynamic_cast<Item*> (s));
70 Beam::get_thickness (Grob * me)
72 SCM th = me->get_grob_property ("thickness");
74 return gh_scm2double (th)* Staff_symbol_referencer::staff_space (me);
79 /* Return the translation between 2 adjoining beams. */
81 Beam::get_beam_translation (Grob *me)
83 SCM func = me->get_grob_property ("space-function");
84 SCM s = gh_call2 (func, me->self_scm (), scm_int2num (get_beam_count (me)));
85 return gh_scm2double (s);
88 /* Maximum beam_count. */
90 Beam::get_beam_count (Grob *me)
93 for (SCM s = me->get_grob_property ("stems"); gh_pair_p (s); s = ly_cdr (s))
95 Grob *stem = unsmob_grob (ly_car (s));
96 m = m >? (Stem::beam_multiplicity (stem).length () + 1);
103 Space return space between beams.
105 MAKE_SCHEME_CALLBACK (Beam, space_function, 2);
107 Beam::space_function (SCM smob, SCM beam_count)
109 Grob *me = unsmob_grob (smob);
111 Real staff_space = Staff_symbol_referencer::staff_space (me);
112 Real line = me->get_paper ()->get_realvar (ly_symbol2scm ("linethickness"));
113 Real thickness = get_thickness (me);
115 Real beam_translation = gh_scm2int (beam_count) < 4
116 ? (2*staff_space + line - thickness) / 2.0
117 : (3*staff_space + line - thickness) / 3.0;
119 return gh_double2scm (beam_translation);
123 /* After pre-processing all directions should be set.
124 Several post-processing routines (stem, slur, script) need stem/beam
126 Currenly, this means that beam has set all stem's directions.
127 [Alternatively, stems could set its own directions, according to
128 their beam, during 'final-pre-processing'.] */
129 MAKE_SCHEME_CALLBACK (Beam, before_line_breaking, 1);
131 Beam::before_line_breaking (SCM smob)
133 Grob *me = unsmob_grob (smob);
135 /* Beams with less than 2 two stems don't make much sense, but could happen
140 For a beam that only has one stem, we try to do some disappearance magic:
141 we revert the flag, and move on to The Eternal Engraving Fields. */
143 int count = visible_stem_count (me);
146 me->warning (_ ("beam has less than two visible stems"));
148 SCM stems = me->get_grob_property ("stems");
149 if (scm_ilength (stems) == 1)
151 me->warning (_ ("Beam has less than two stems. Removing beam."));
153 unsmob_grob (gh_car (stems))->set_grob_property ("beam", SCM_EOL);
156 return SCM_UNSPECIFIED;
158 else if (scm_ilength (stems) == 0)
161 return SCM_UNSPECIFIED;
166 Direction d = get_default_dir (me);
168 consider_auto_knees (me);
169 set_stem_directions (me, d);
173 set_stem_shorten (me);
181 We want a maximal number of shared beams, but if there is choice, we
182 take the one that is closest to the end of the stem. This is for situations like
195 position_with_maximal_common_beams (SCM left_beaming, SCM right_beaming,
199 Slice lslice = int_list_to_slice (gh_cdr (left_beaming));
203 for (int i = lslice[-left_dir];
204 (i - lslice[left_dir])* left_dir <= 0 ; i+= left_dir)
207 for ( SCM s = gh_car (right_beaming); gh_pair_p (s); s = gh_cdr (s))
209 int k = - right_dir * gh_scm2int (gh_car (s)) + i;
210 if (scm_memq (scm_int2num (k), left_beaming) != SCM_BOOL_F)
214 if (count >= best_count)
225 Beam::connect_beams (Grob *me)
227 Link_array<Grob> stems=
228 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
231 last_int.set_empty();
232 SCM last_beaming = SCM_EOL;
233 Direction last_dir = CENTER;
234 for (int i = 0; i< stems.size(); i++)
236 Grob *this_stem = stems[i];
237 SCM this_beaming = this_stem->get_grob_property ("beaming");
239 Direction this_dir = Directional_element_interface::get(this_stem);
240 if (gh_pair_p (last_beaming) && gh_pair_p (this_beaming))
242 int start_point = position_with_maximal_common_beams
243 (last_beaming, this_beaming,
250 if (d == RIGHT && i == stems.size()-1)
253 new_slice.set_empty();
254 SCM s = index_get_cell (this_beaming, d);
255 for (; gh_pair_p (s); s = gh_cdr (s))
258 start_point - this_dir * gh_scm2int (gh_car (s));
260 new_slice.add_point (new_beam_pos);
261 gh_set_car_x (s, scm_int2num (new_beam_pos));
266 while (flip (&d) != LEFT);
268 if (!new_slice.empty_b())
269 last_int = new_slice;
273 gh_set_car_x ( this_beaming, SCM_EOL);
274 SCM s = gh_cdr (this_beaming);
275 for (; gh_pair_p (s); s = gh_cdr (s))
277 int np = - this_dir * gh_scm2int (gh_car(s));
278 gh_set_car_x (s, scm_int2num (np));
279 last_int.add_point (np);
283 if (i == stems.size () -1)
285 gh_set_cdr_x (this_beaming, SCM_EOL);
288 if (scm_ilength (gh_cdr (this_beaming)) > 0)
290 last_beaming = this_beaming;
296 MAKE_SCHEME_CALLBACK (Beam, brew_molecule, 1);
298 Beam::brew_molecule (SCM grob)
300 Grob *me = unsmob_grob (grob);
301 Link_array<Grob> stems=
302 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
303 Grob* xcommon = common_refpoint_of_array (stems, me, X_AXIS);
306 if (visible_stem_count (me))
308 // ugh -> use commonx
309 x0 = first_visible_stem (me)->relative_coordinate (xcommon, X_AXIS);
310 dx = last_visible_stem (me)->relative_coordinate (xcommon, X_AXIS) - x0;
314 x0 = stems[0]->relative_coordinate (xcommon, X_AXIS);
315 dx = stems.top ()->relative_coordinate (xcommon, X_AXIS) - x0;
318 SCM posns = me->get_grob_property ("positions");
320 if (!ly_number_pair_p (posns))
322 programming_error ("No beam posns");
323 pos = Interval (0,0);
326 pos= ly_scm2interval (posns);
328 Real dy = pos.delta ();
329 Real dydx = dy && dx ? dy/dx : 0;
331 Real thick = get_thickness (me);
332 Real bdy = get_beam_translation (me);
334 SCM last_beaming = SCM_EOL;;
335 Real last_xposn = -1;
336 Real last_width = -1 ;
339 SCM gap = me->get_grob_property ("gap");
341 Real lt = me->get_paper ()->get_realvar (ly_symbol2scm ("linethickness"));
343 for (int i = 0; i<= stems.size(); i++)
345 Grob * st = (i < stems.size()) ? stems[i] : 0;
347 SCM this_beaming = st ? st->get_grob_property ("beaming") : SCM_EOL;
348 Real xposn = st ? st->relative_coordinate (xcommon, X_AXIS) : 0.0;
349 Real stem_width = st ? gh_scm2double (st->get_grob_property ("thickness")) *lt : 0 ;
352 We do the space left of ST, with lfliebertjes pointing to the
353 right from the left stem, and rfliebertjes pointing left from
356 SCM left = (i>0) ? gh_cdr (last_beaming) : SCM_EOL;
357 SCM right = st ? gh_car (this_beaming) : SCM_EOL;
359 Array<int> fullbeams;
360 Array<int> lfliebertjes;
361 Array<int> rfliebertjes;
364 gh_pair_p (s); s =gh_cdr (s))
366 int b = gh_scm2int (gh_car (s));
367 if (scm_memq (gh_car(s), right) != SCM_BOOL_F)
373 lfliebertjes.push (b);
377 gh_pair_p (s); s =gh_cdr (s))
379 int b = gh_scm2int (gh_car (s));
380 if (scm_memq (gh_car(s), left) == SCM_BOOL_F)
382 rfliebertjes.push (b);
387 how much to stick out for beams across linebreaks
389 Real break_overshoot = 3.0;
390 Real w = (i>0 && st)? xposn - last_xposn : break_overshoot;
391 Real stem_offset = 0.0;
392 Real width_corr = 0.0;
395 stem_offset -= last_width/2;
396 width_corr += last_width/2;
399 if (i == stems.size() -1)
401 width_corr += stem_width/2;
404 if (gh_number_p (gap))
406 Real g = gh_scm2double (gap);
411 Molecule whole = Lookup::beam (dydx, w + width_corr, thick);
412 for (int j = fullbeams.size(); j--;)
415 b.translate_axis (last_xposn - x0 + stem_offset, X_AXIS);
416 b.translate_axis (dydx * (last_xposn - x0) + bdy * fullbeams[j], Y_AXIS);
417 the_beam.add_molecule (b);
420 if (lfliebertjes.size() || rfliebertjes.size())
426 int t = Stem::duration_log (st);
428 SCM proc = me->get_grob_property ("flag-width-function");
429 SCM result = gh_call1 (proc, scm_int2num (t));
430 nw_f = gh_scm2double (result);
433 nw_f = break_overshoot;
435 /* Half beam should be one note-width,
436 but let's make sure two half-beams never touch */
437 Real w = (i>0 && st) ? (xposn - last_xposn) : break_overshoot;
440 Molecule half = Lookup::beam (dydx, w, thick);
441 for (int j = lfliebertjes.size(); j--;)
444 b.translate_axis (last_xposn - x0, X_AXIS);
445 b.translate_axis (dydx * (last_xposn-x0) + bdy * lfliebertjes[j], Y_AXIS);
446 the_beam.add_molecule (b);
448 for (int j = rfliebertjes.size(); j--;)
451 b.translate_axis (xposn - x0 - w , X_AXIS);
452 b.translate_axis (dydx * (xposn-x0 -w) + bdy * rfliebertjes[j], Y_AXIS);
453 the_beam.add_molecule (b);
459 last_width = stem_width;
460 last_beaming = this_beaming;
463 the_beam.translate_axis (x0 - me->relative_coordinate (xcommon, X_AXIS), X_AXIS);
464 the_beam.translate_axis (pos[LEFT], Y_AXIS);
469 This code prints the demerits for each beam. Perhaps this
470 should be switchable for those who want to twiddle with the
476 str += to_string (gh_scm2int (me->get_grob_property ("best-idx")));
479 str += to_string (gh_scm2double (me->get_grob_property ("quant-score")),
482 SCM properties = Font_interface::font_alist_chain (me);
484 Molecule tm = Text_item::interpret_new_markup
485 (me->self_scm(), properties, scm_makfrom0str (str.to_str0 ()));
486 the_beam.add_at_edge (Y_AXIS, UP, tm, 5.0, 0);
492 return the_beam.smobbed_copy();
499 Beam::get_default_dir (Grob *me)
501 Drul_array<int> total;
502 total[UP] = total[DOWN] = 0;
503 Drul_array<int> count;
504 count[UP] = count[DOWN] = 0;
507 Link_array<Grob> stems=
508 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
510 for (int i=0; i <stems.size (); i++)
513 Direction sd = Directional_element_interface::get (s);
515 int center_distance = int(- d * Stem::head_positions (s) [-d]) >? 0;
516 int current = sd ? (1 + d * sd)/2 : center_distance;
523 } while (flip (&d) != DOWN);
525 SCM func = me->get_grob_property ("dir-function");
526 SCM s = gh_call2 (func,
527 gh_cons (scm_int2num (count[UP]),
528 scm_int2num (count[DOWN])),
529 gh_cons (scm_int2num (total[UP]),
530 scm_int2num (total[DOWN])));
532 if (gh_number_p (s) && gh_scm2int (s))
535 /* If dir is not determined: get default */
536 return to_dir (me->get_grob_property ("neutral-direction"));
540 /* Set all stems with non-forced direction to beam direction.
541 Urg: non-forced should become `without/with unforced' direction,
542 once stem gets cleaned-up. */
544 Beam::set_stem_directions (Grob *me, Direction d)
546 Link_array<Grob> stems
547 =Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
549 for (int i=0; i <stems.size (); i++)
553 SCM forcedir = s->get_grob_property ("direction");
554 if (!to_dir (forcedir))
555 Directional_element_interface::set (s, d);
560 A union of intervals in the real line.
562 Abysmal performance (quadratic) for large N, hopefully we don't have
563 that large N. In any case, this should probably be rewritten to use
568 Array<Interval> allowed_regions_;
577 allowed_regions_.clear();
580 allowed_regions_.push (s);
583 void remove_interval (Interval rm)
585 for (int i = 0; i < allowed_regions_.size(); )
589 s.intersect (allowed_regions_[i]);
593 Interval before = allowed_regions_[i];
594 Interval after = allowed_regions_[i];
596 before[RIGHT] = s[LEFT];
597 after[LEFT] = s[RIGHT];
599 if (!before.empty_b() && before.length () > 0.0)
601 allowed_regions_.insert (before, i);
604 allowed_regions_.del (i);
605 if (!after.empty_b () && after.length () > 0.0)
607 allowed_regions_.insert (after, i);
619 Only try horizontal beams for knees. No reliable detection of
620 anything else is possible here, since we don't know funky-beaming
621 settings, or X-distances (slopes!) People that want sloped
622 knee-beams, should set the directions manually.
625 Beam::consider_auto_knees (Grob* me)
627 SCM scm = me->get_grob_property ("auto-knee-gap");
628 if (!gh_number_p (scm))
631 Real threshold = gh_scm2double (scm);
637 Link_array<Grob> stems=
638 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
640 Grob *common = common_refpoint_of_array (stems, me, Y_AXIS);
641 Real staff_space = Staff_symbol_referencer::staff_space (me);
643 Array<Interval> hps_array;
644 for (int i=0; i < stems.size (); i++)
646 Grob* stem = stems[i];
647 if (Stem::invisible_b (stem))
650 Interval hps = Stem::head_positions (stem);
655 hps *= staff_space * 0.5 ;
658 We could subtract beam Y position, but this routine only
659 sets stem directions, a constant shift does not have an
663 hps += stem->relative_coordinate (common, Y_AXIS);
665 if (to_dir (stem->get_grob_property ("direction")))
667 Direction stemdir = to_dir (stem->get_grob_property ("direction"));
668 hps[-stemdir] = - stemdir * infinity_f;
671 hps_array.push (hps);
673 gaps.remove_interval (hps);
677 Real max_gap_len =0.0;
679 for (int i = gaps.allowed_regions_.size() -1; i >= 0 ; i--)
681 Interval gap = gaps.allowed_regions_[i];
684 the outer gaps are not knees.
686 if (isinf (gap[LEFT]) || isinf(gap[RIGHT]))
689 if (gap.length () >= max_gap_len)
691 max_gap_len = gap.length();
696 if (max_gap_len > threshold)
699 for (int i = 0; i < stems.size(); i++)
701 Grob* stem = stems[i];
702 if (Stem::invisible_b (stem))
705 Interval hps = hps_array[j++];
708 Direction d = (hps.center () < max_gap.center()) ?
711 stem->set_grob_property ("direction", scm_int2num (d));
713 hps.intersect (max_gap);
714 assert (hps.empty_b () || hps.length () < 1e-6 );
721 /* Set stem's shorten property if unset.
724 take some y-position (chord/beam/nearest?) into account
725 scmify forced-fraction
727 This is done in beam because the shorten has to be uniform over the
732 Beam::set_stem_shorten (Grob *me)
735 shortening looks silly for x staff beams
740 Real forced_fraction = 1.0 * forced_stem_count (me)
741 / visible_stem_count (me);
743 int beam_count = get_beam_count (me);
745 SCM shorten_list = me->get_grob_property ("beamed-stem-shorten");
746 if (shorten_list == SCM_EOL)
749 Real staff_space = Staff_symbol_referencer::staff_space (me);
752 robust_list_ref (beam_count -1, shorten_list);
753 Real shorten_f = gh_scm2double (shorten_elt) * staff_space;
755 /* your similar cute comment here */
756 shorten_f *= forced_fraction;
759 me->set_grob_property ("shorten", gh_double2scm (shorten_f));
762 /* Call list of y-dy-callbacks, that handle setting of
766 MAKE_SCHEME_CALLBACK (Beam, after_line_breaking, 1);
768 Beam::after_line_breaking (SCM smob)
770 Grob *me = unsmob_grob (smob);
772 /* Copy to mutable list. */
773 SCM s = ly_deep_copy (me->get_grob_property ("positions"));
774 me->set_grob_property ("positions", s);
776 if (ly_car (s) == SCM_BOOL_F)
779 // one wonders if such genericity is necessary --hwn.
780 SCM callbacks = me->get_grob_property ("position-callbacks");
781 for (SCM i = callbacks; gh_pair_p (i); i = ly_cdr (i))
782 gh_call1 (ly_car (i), smob);
785 set_stem_lengths (me);
786 return SCM_UNSPECIFIED;
791 Compute a first approximation to the beam slope.
793 MAKE_SCHEME_CALLBACK (Beam, least_squares, 1);
795 Beam::least_squares (SCM smob)
797 Grob *me = unsmob_grob (smob);
799 int count = visible_stem_count (me);
804 me->set_grob_property ("positions", ly_interval2scm (pos));
805 return SCM_UNSPECIFIED;
809 Array<Real> x_posns ;
810 Link_array<Grob> stems=
811 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
812 Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
813 Grob *commony = common_refpoint_of_array (stems, me, Y_AXIS);
815 Real my_y = me->relative_coordinate (commony, Y_AXIS);
817 Grob *fvs = first_visible_stem (me);
818 Grob *lvs = last_visible_stem (me);
820 Interval ideal (Stem::get_stem_info (fvs).ideal_y_
821 + fvs->relative_coordinate (commony, Y_AXIS) -my_y,
822 Stem::get_stem_info (lvs).ideal_y_
823 + lvs->relative_coordinate (commony, Y_AXIS) - my_y);
825 Real x0 = first_visible_stem (me)->relative_coordinate (commonx, X_AXIS);
826 for (int i=0; i < stems.size (); i++)
830 Real x = s->relative_coordinate (commonx, X_AXIS) - x0;
833 Real dx = last_visible_stem (me)->relative_coordinate (commonx, X_AXIS) - x0;
842 Interval chord (Stem::chord_start_y (first_visible_stem (me)),
843 Stem::chord_start_y (last_visible_stem (me)));
845 /* Simple beams (2 stems) on middle line should be allowed to be
848 However, if both stems reach middle line,
849 ideal[LEFT] == ideal[RIGHT] and ideal.delta () == 0.
851 For that case, we apply artificial slope */
852 if (!ideal[LEFT] && chord.delta () && count == 2)
855 Direction d = (Direction) (sign (chord.delta ()) * UP);
856 pos[d] = gh_scm2double (me->get_grob_property ("thickness")) / 2;
865 For broken beams this doesn't work well. In this case, the
866 slope esp. of the first part of a broken beam should predict
867 where the second part goes.
879 Array<Offset> ideals;
880 for (int i=0; i < stems.size (); i++)
883 if (Stem::invisible_b (s))
885 ideals.push (Offset (x_posns[i],
886 Stem::get_stem_info (s).ideal_y_
887 + s->relative_coordinate (commony, Y_AXIS)
891 minimise_least_squares (&dydx, &y, ideals);
894 me->set_grob_property ("least-squares-dy", gh_double2scm (dy));
895 pos = Interval (y, (y+dy));
898 me->set_grob_property ("positions", ly_interval2scm (pos));
900 return SCM_UNSPECIFIED;
905 We can't combine with previous function, since check concave and
906 slope damping comes first.
908 TODO: we should use the concaveness to control the amount of damping
912 MAKE_SCHEME_CALLBACK (Beam, shift_region_to_valid, 1);
914 Beam::shift_region_to_valid (SCM grob)
916 Grob *me = unsmob_grob (grob);
920 Array<Real> x_posns ;
921 Link_array<Grob> stems=
922 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
923 Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
924 Grob *commony = common_refpoint_of_array (stems, me, Y_AXIS);
926 Grob *fvs = first_visible_stem (me);
929 return SCM_UNSPECIFIED;
931 Real x0 =fvs->relative_coordinate (commonx, X_AXIS);
932 for (int i=0; i < stems.size (); i++)
936 Real x = s->relative_coordinate (commonx, X_AXIS) - x0;
940 Grob *lvs = last_visible_stem (me);
942 return SCM_UNSPECIFIED;
944 Real dx = lvs->relative_coordinate (commonx, X_AXIS) - x0;
946 Interval pos = ly_scm2interval ( me->get_grob_property ("positions"));
947 Real dy = pos.delta();
953 Shift the positions so that we have a chance of finding good
954 quants (i.e. no short stem failures.)
956 Interval feasible_left_point;
957 feasible_left_point.set_full ();
958 for (int i=0; i < stems.size (); i++)
961 if (Stem::invisible_b (s))
964 Direction d = Stem::get_direction (s);
967 Stem::get_stem_info (s).shortest_y_
968 - dydx * x_posns [i];
971 left_y is now relative to the stem S. We want relative to
972 ourselves, so translate:
975 + s->relative_coordinate (commony, Y_AXIS)
976 - me->relative_coordinate (commony, Y_AXIS);
982 feasible_left_point.intersect (flp);
985 if (feasible_left_point.empty_b())
987 warning (_("Not sure that we can find a nice beam slope (no viable initial configuration found)."));
989 else if (!feasible_left_point.elem_b(y))
991 if (isinf (feasible_left_point[DOWN]))
992 y = feasible_left_point[UP] - REGION_SIZE;
993 else if (isinf (feasible_left_point[UP]))
994 y = feasible_left_point[DOWN]+ REGION_SIZE;
996 y = feasible_left_point.center ();
998 pos = Interval (y, (y+dy));
999 me->set_grob_property ("positions", ly_interval2scm (pos));
1000 return SCM_UNSPECIFIED;
1004 MAKE_SCHEME_CALLBACK (Beam, check_concave, 1);
1006 Beam::check_concave (SCM smob)
1008 Grob *me = unsmob_grob (smob);
1010 Link_array<Grob> stems =
1011 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1013 for (int i = 0; i < stems.size ();)
1015 if (Stem::invisible_b (stems[i]))
1021 if (stems.size () < 3)
1022 return SCM_UNSPECIFIED;
1025 /* Concaveness #1: If distance of an inner notehead to line between
1026 two outer noteheads is bigger than CONCAVENESS-GAP (2.0ss),
1027 beam is concave (Heinz Stolba).
1029 In the case of knees, the line connecting outer heads is often
1030 not related to the beam slope (it may even go in the other
1031 direction). Skip the check when the outer stems point in
1032 different directions. --hwn
1035 bool concaveness1 = false;
1036 SCM gap = me->get_grob_property ("concaveness-gap");
1037 if (gh_number_p (gap)
1038 && Stem::get_direction(stems.top ())
1039 == Stem::get_direction(stems[0]))
1041 Real r1 = gh_scm2double (gap);
1042 Real dy = Stem::chord_start_y (stems.top ())
1043 - Stem::chord_start_y (stems[0]);
1046 Real slope = dy / (stems.size () - 1);
1048 Real y0 = Stem::chord_start_y (stems[0]);
1049 for (int i = 1; i < stems.size () - 1; i++)
1051 Real c = (Stem::chord_start_y (stems[i]) - y0) - i * slope;
1054 concaveness1 = true;
1061 /* Concaveness #2: Sum distances of inner noteheads that fall
1062 outside the interval of the two outer noteheads.
1064 We only do this for beams where first and last stem have the same
1068 Note that "convex" stems compensate for "concave" stems.
1069 (is that intentional?) --hwn.
1072 Real concaveness2 = 0;
1073 SCM thresh = me->get_grob_property ("concaveness-threshold");
1074 Real r2 = infinity_f;
1075 if (!concaveness1 && gh_number_p (thresh)
1076 && Stem::get_direction(stems.top ())
1077 == Stem::get_direction(stems[0]))
1079 r2 = gh_scm2double (thresh);
1081 Direction dir = Stem::get_direction(stems.top ());
1083 Interval iv (Stem::chord_start_y (stems[0]),
1084 Stem::chord_start_y (stems.top ()));
1086 if (iv[MAX] < iv[MIN])
1089 for (int i = 1; i < stems.size () - 1; i++)
1091 Real f = Stem::chord_start_y (stems[i]);
1092 concave += ((f - iv[MAX] ) >? 0) +
1093 ((f - iv[MIN] ) <? 0);
1096 concaveness2 = concave / (stems.size () - 2);
1100 ugh: this is the a kludge to get
1101 input/regression/beam-concave.ly to behave as
1107 huh? we're dividing twice (which is not scalable) meaning that
1108 the longer the beam, the more unlikely it will be
1109 concave. Maybe you would even expect the other way around??
1114 concaveness2 /= (stems.size () - 2);
1117 /* TODO: some sort of damping iso -> plain horizontal */
1118 if (concaveness1 || concaveness2 > r2)
1120 Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
1121 Real r = pos.linear_combination (0);
1122 me->set_grob_property ("positions", ly_interval2scm (Interval (r, r)));
1123 me->set_grob_property ("least-squares-dy", gh_double2scm (0));
1126 return SCM_UNSPECIFIED;
1129 /* This neat trick is by Werner Lemberg,
1130 damped = tanh (slope)
1131 corresponds with some tables in [Wanske] CHECKME */
1132 MAKE_SCHEME_CALLBACK (Beam, slope_damping, 1);
1134 Beam::slope_damping (SCM smob)
1136 Grob *me = unsmob_grob (smob);
1138 if (visible_stem_count (me) <= 1)
1139 return SCM_UNSPECIFIED;
1141 SCM s = me->get_grob_property ("damping");
1142 int damping = gh_scm2int (s);
1146 Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
1147 Real dy = pos.delta ();
1149 Grob *fvs = first_visible_stem (me);
1150 Grob *lvs = last_visible_stem (me);
1152 Grob *commonx = fvs->common_refpoint (lvs, X_AXIS);
1155 Real dx = last_visible_stem (me)->relative_coordinate (commonx, X_AXIS)
1156 - first_visible_stem (me)->relative_coordinate (commonx, X_AXIS);
1157 Real dydx = dy && dx ? dy/dx : 0;
1158 dydx = 0.6 * tanh (dydx) / damping;
1160 Real damped_dy = dydx * dx;
1161 pos[LEFT] += (dy - damped_dy) / 2;
1162 pos[RIGHT] -= (dy - damped_dy) / 2;
1164 me->set_grob_property ("positions", ly_interval2scm (pos));
1166 return SCM_UNSPECIFIED;
1170 Report slice containing the numbers that are both in (car BEAMING)
1174 where_are_the_whole_beams(SCM beaming)
1178 for( SCM s = gh_car (beaming); gh_pair_p (s) ; s = gh_cdr (s))
1180 if (scm_memq (gh_car (s), gh_cdr (beaming)) != SCM_BOOL_F)
1182 l.add_point (gh_scm2int (gh_car (s)));
1188 /* Return the Y position of the stem-end, given the Y-left, Y-right
1189 in POS for stem S. This Y position is relative to S. */
1191 Beam::calc_stem_y (Grob *me, Grob* s, Grob ** common,
1193 Interval pos, bool french)
1195 Real beam_translation = get_beam_translation (me);
1198 Real r = s->relative_coordinate (common[X_AXIS], X_AXIS) - xl;
1199 Real dy = pos.delta ();
1201 Real stem_y_beam0 = (dy && dx
1206 Direction my_dir = Directional_element_interface::get (s);
1207 SCM beaming = s->get_grob_property ("beaming");
1209 Real stem_y = stem_y_beam0;
1212 Slice bm = where_are_the_whole_beams (beaming);
1214 stem_y += beam_translation * bm[-my_dir];
1218 Slice bm = Stem::beam_multiplicity(s);
1220 stem_y +=bm[my_dir] * beam_translation;
1223 Real id = me->relative_coordinate (common[Y_AXIS], Y_AXIS)
1224 - s->relative_coordinate (common[Y_AXIS], Y_AXIS);
1230 Hmm. At this time, beam position and slope are determined. Maybe,
1231 stem directions and length should set to relative to the chord's
1232 position of the beam. */
1234 Beam::set_stem_lengths (Grob *me)
1236 Link_array<Grob> stems=
1237 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
1243 for (int a = 2; a--;)
1244 common[a] = common_refpoint_of_array (stems, me, Axis(a));
1246 Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
1247 Real staff_space = Staff_symbol_referencer::staff_space (me);
1251 if (gh_number_p (me->get_grob_property ("gap"))
1252 &&gh_scm2double (me->get_grob_property ("gap")))
1255 thick = get_thickness(me);
1258 // ugh -> use commonx
1259 Grob * fvs = first_visible_stem (me);
1260 Grob *lvs = last_visible_stem (me);
1262 Real xl = fvs ? fvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
1263 Real xr = lvs ? lvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
1265 for (int i=0; i < stems.size (); i++)
1268 if (Stem::invisible_b (s))
1271 bool french = to_boolean (s->get_grob_property ("french-beaming"));
1272 Real stem_y = calc_stem_y (me, s, common,
1274 pos, french && s != lvs && s!= fvs);
1277 Make the stems go up to the end of the beam. This doesn't matter
1278 for normal beams, but for tremolo beams it looks silly otherwise.
1281 stem_y += thick * 0.5 * Directional_element_interface::get(s);
1283 Stem::set_stemend (s, 2* stem_y / staff_space);
1288 Beam::set_beaming (Grob *me, Beaming_info_list *beaming)
1290 Link_array<Grob> stems=
1291 Pointer_group_interface__extract_grobs (me, (Grob *)0, "stems");
1294 for (int i=0; i < stems.size (); i++)
1297 Don't overwrite user settings.
1302 /* Don't set beaming for outside of outer stems */
1303 if ((d == LEFT && i == 0)
1304 ||(d == RIGHT && i == stems.size () -1))
1307 Grob *st = stems[i];
1308 SCM beaming_prop = st->get_grob_property ("beaming");
1309 if (beaming_prop == SCM_EOL ||
1310 index_get_cell (beaming_prop, d) == SCM_EOL)
1312 int b = beaming->infos_.elem (i).beams_i_drul_[d];
1314 && i < stems.size() -1
1315 && Stem::invisible_b (st))
1316 b = b <? beaming->infos_.elem(i).beams_i_drul_[-d];
1318 Stem::set_beaming (st, b, d);
1321 while (flip (&d) != LEFT);
1326 Beam::forced_stem_count (Grob *me)
1328 Link_array<Grob>stems =
1329 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1331 for (int i=0; i < stems.size (); i++)
1335 if (Stem::invisible_b (s))
1338 /* I can imagine counting those boundaries as a half forced stem,
1339 but let's count them full for now. */
1340 if (abs (Stem::chord_start_y (s)) > 0.1
1341 && (Stem::get_direction (s) != Stem::get_default_dir (s)))
1351 Beam::visible_stem_count (Grob *me)
1353 Link_array<Grob>stems =
1354 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1356 for (int i = stems.size (); i--;)
1358 if (!Stem::invisible_b (stems[i]))
1365 Beam::first_visible_stem (Grob *me)
1367 Link_array<Grob>stems =
1368 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1370 for (int i = 0; i < stems.size (); i++)
1372 if (!Stem::invisible_b (stems[i]))
1379 Beam::last_visible_stem (Grob *me)
1381 Link_array<Grob>stems =
1382 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1383 for (int i = stems.size (); i--;)
1385 if (!Stem::invisible_b (stems[i]))
1395 handle rest under beam (do_post: beams are calculated now)
1396 what about combination of collisions and rest under beam.
1400 rest -> stem -> beam -> interpolate_y_position ()
1402 MAKE_SCHEME_CALLBACK (Beam, rest_collision_callback, 2);
1404 Beam::rest_collision_callback (SCM element_smob, SCM axis)
1406 Grob *rest = unsmob_grob (element_smob);
1407 Axis a = (Axis) gh_scm2int (axis);
1409 assert (a == Y_AXIS);
1411 Grob *st = unsmob_grob (rest->get_grob_property ("stem"));
1414 return gh_double2scm (0.0);
1415 Grob *beam = unsmob_grob (stem->get_grob_property ("beam"));
1417 || !Beam::has_interface (beam)
1418 || !Beam::visible_stem_count (beam))
1419 return gh_double2scm (0.0);
1421 Interval pos (0, 0);
1422 SCM s = beam->get_grob_property ("positions");
1423 if (gh_pair_p (s) && gh_number_p (ly_car (s)))
1424 pos = ly_scm2interval (s);
1426 Real dy = pos.delta ();
1427 // ugh -> use commonx
1428 Real x0 = first_visible_stem (beam)->relative_coordinate (0, X_AXIS);
1429 Real dx = last_visible_stem (beam)->relative_coordinate (0, X_AXIS) - x0;
1430 Real dydx = dy && dx ? dy/dx : 0;
1432 Direction d = Stem::get_direction (stem);
1433 Real stem_y = (pos[LEFT]
1434 + (stem->relative_coordinate (0, X_AXIS) - x0) * dydx)
1437 Real beam_translation = get_beam_translation (beam);
1438 Real beam_thickness = gh_scm2double (beam->get_grob_property ("thickness"));
1439 int beam_count = get_direction_beam_count (beam, d);
1440 Real height_of_my_beams = beam_thickness
1441 + (beam_count - 1) * beam_translation;
1442 Real beam_y = stem_y - height_of_my_beams + beam_thickness / 2.0;
1444 Real staff_space = Staff_symbol_referencer::staff_space (rest);
1446 /* Better calculate relative-distance directly, rather than using
1448 Grob *common_x = rest->common_refpoint (beam, Y_AXIS);
1449 Real rest_dim = rest->extent (common_x, Y_AXIS)[d] / staff_space * d;
1451 Real minimum_distance = gh_scm2double
1452 (rest->get_grob_property ("minimum-beam-collision-distance"));
1454 Real distance = beam_y - rest_dim;
1457 shift = minimum_distance - distance;
1458 else if (minimum_distance > distance)
1459 shift = minimum_distance - distance;
1461 int stafflines = Staff_symbol_referencer::line_count (rest);
1463 /* Always move discretely by half spaces */
1464 Real discrete_shift = ceil (shift * 2.0) / 2.0;
1466 /* Inside staff, move by whole spaces*/
1467 if ((rest->extent (common_x, Y_AXIS)[d] + discrete_shift) * d
1469 ||(rest->extent (common_x, Y_AXIS)[-d] + discrete_shift) * -d
1471 discrete_shift = ceil (discrete_shift);
1473 return gh_double2scm (-d * discrete_shift);
1477 Beam::knee_b (Grob* me)
1479 SCM k = me->get_grob_property ("knee");
1480 if (gh_boolean_p (k))
1481 return gh_scm2bool (k);
1485 for (SCM s = me->get_grob_property ("stems"); gh_pair_p (s); s = ly_cdr (s))
1487 Direction dir = Directional_element_interface::get
1488 (unsmob_grob (ly_car (s)));
1497 me->set_grob_property ("knee", gh_bool2scm (knee));
1503 Beam::get_direction_beam_count (Grob *me, Direction d )
1505 Link_array<Grob>stems =
1506 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1509 for (int i = stems.size (); i--;)
1512 Should we take invisible stems into account?
1514 if (Stem::get_direction (stems[i]) == d)
1515 bc = bc >? (Stem::beam_multiplicity (stems[i]).length () + 1);
1522 ADD_INTERFACE (Beam, "beam-interface",
1525 "#'thickness= weight of beams, in staffspace "
1528 "We take the least squares line through the ideal-length stems, and "
1529 "then damp that using "
1531 " damped = tanh (slope) \n"
1533 "this gives an unquantized left and right position for the beam end. "
1534 "Then we take all combinations of quantings near these left and right "
1535 "positions, and give them a score (according to how close they are to "
1536 "the ideal slope, how close the result is to the ideal stems, etc.). We "
1537 "take the best scoring combination. "
1539 "knee position-callbacks concaveness-gap concaveness-threshold dir-function quant-score auto-knee-gap gap chord-tremolo beamed-stem-shorten shorten least-squares-dy damping flag-width-function neutral-direction positions space-function thickness");