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.
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.
31 #include "molecule.hh"
32 #include "directional-element-interface.hh"
36 #include "least-squares.hh"
38 #include "paper-def.hh"
40 #include "group-interface.hh"
41 #include "staff-symbol-referencer.hh"
48 bool debug_beam_quanting_flag;
52 #include "text-item.hh" // debug output.
53 #include "font-interface.hh" // debug output.
58 Beam::add_stem (Grob *me, Grob *s)
60 Pointer_group_interface::add_grob (me, ly_symbol2scm ("stems"), s);
62 s->add_dependency (me);
64 assert (!Stem::get_beam (s));
65 s->set_grob_property ("beam", me->self_scm ());
67 add_bound_item (dynamic_cast<Spanner*> (me), dynamic_cast<Item*> (s));
72 Beam::get_thickness (Grob * me)
74 SCM th = me->get_grob_property ("thickness");
76 return gh_scm2double (th)* Staff_symbol_referencer::staff_space (me);
81 /* Return the translation between 2 adjoining beams. */
83 Beam::get_beam_translation (Grob *me)
85 SCM func = me->get_grob_property ("space-function");
86 SCM s = gh_call2 (func, me->self_scm (), scm_int2num (get_beam_count (me)));
87 return gh_scm2double (s);
90 /* Maximum beam_count. */
92 Beam::get_beam_count (Grob *me)
95 for (SCM s = me->get_grob_property ("stems"); gh_pair_p (s); s = ly_cdr (s))
97 Grob *stem = unsmob_grob (ly_car (s));
98 m = m >? (Stem::beam_multiplicity (stem).length () + 1);
105 Space return space between beams.
107 MAKE_SCHEME_CALLBACK (Beam, space_function, 2);
109 Beam::space_function (SCM smob, SCM beam_count)
111 Grob *me = unsmob_grob (smob);
113 Real staff_space = Staff_symbol_referencer::staff_space (me);
114 Real line = me->get_paper ()->get_realvar (ly_symbol2scm ("linethickness"));
115 Real thickness = get_thickness (me);
117 Real beam_translation = gh_scm2int (beam_count) < 4
118 ? (2*staff_space + line - thickness) / 2.0
119 : (3*staff_space + line - thickness) / 3.0;
121 return gh_double2scm (beam_translation);
125 /* After pre-processing all directions should be set.
126 Several post-processing routines (stem, slur, script) need stem/beam
128 Currenly, this means that beam has set all stem's directions.
129 [Alternatively, stems could set its own directions, according to
130 their beam, during 'final-pre-processing'.] */
131 MAKE_SCHEME_CALLBACK (Beam, before_line_breaking, 1);
133 Beam::before_line_breaking (SCM smob)
135 Grob *me = unsmob_grob (smob);
137 /* Beams with less than 2 two stems don't make much sense, but could happen
142 For a beam that only has one stem, we try to do some disappearance magic:
143 we revert the flag, and move on to The Eternal Engraving Fields. */
145 int count = visible_stem_count (me);
148 me->warning (_ ("beam has less than two visible stems"));
150 SCM stems = me->get_grob_property ("stems");
151 if (scm_ilength (stems) == 1)
153 me->warning (_ ("Beam has less than two stems. Removing beam."));
155 unsmob_grob (gh_car (stems))->set_grob_property ("beam", SCM_EOL);
158 return SCM_UNSPECIFIED;
160 else if (scm_ilength (stems) == 0)
163 return SCM_UNSPECIFIED;
168 Direction d = get_default_dir (me);
170 consider_auto_knees (me);
171 set_stem_directions (me, d);
175 set_stem_shorten (me);
183 We want a maximal number of shared beams, but if there is choice, we
184 take the one that is closest to the end of the stem. This is for situations like
197 position_with_maximal_common_beams (SCM left_beaming, SCM right_beaming,
201 Slice lslice = int_list_to_slice (gh_cdr (left_beaming));
205 for (int i = lslice[-left_dir];
206 (i - lslice[left_dir])* left_dir <= 0 ; i+= left_dir)
209 for ( SCM s = gh_car (right_beaming); gh_pair_p (s); s = gh_cdr (s))
211 int k = - right_dir * gh_scm2int (gh_car (s)) + i;
212 if (scm_memq (scm_int2num (k), left_beaming) != SCM_BOOL_F)
216 if (count >= best_count)
227 Beam::connect_beams (Grob *me)
229 Link_array<Grob> stems=
230 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
233 last_int.set_empty();
234 SCM last_beaming = SCM_EOL;
235 Direction last_dir = CENTER;
236 for (int i = 0; i< stems.size(); i++)
238 Grob *this_stem = stems[i];
239 SCM this_beaming = this_stem->get_grob_property ("beaming");
241 Direction this_dir = get_grob_direction (this_stem);
242 if (gh_pair_p (last_beaming) && gh_pair_p (this_beaming))
244 int start_point = position_with_maximal_common_beams
245 (last_beaming, this_beaming,
252 if (d == RIGHT && i == stems.size()-1)
255 new_slice.set_empty();
256 SCM s = index_get_cell (this_beaming, d);
257 for (; gh_pair_p (s); s = gh_cdr (s))
260 start_point - this_dir * gh_scm2int (gh_car (s));
262 new_slice.add_point (new_beam_pos);
263 gh_set_car_x (s, scm_int2num (new_beam_pos));
268 while (flip (&d) != LEFT);
270 if (!new_slice.is_empty ())
271 last_int = new_slice;
275 gh_set_car_x ( this_beaming, SCM_EOL);
276 SCM s = gh_cdr (this_beaming);
277 for (; gh_pair_p (s); s = gh_cdr (s))
279 int np = - this_dir * gh_scm2int (gh_car(s));
280 gh_set_car_x (s, scm_int2num (np));
281 last_int.add_point (np);
285 if (i == stems.size () -1)
287 gh_set_cdr_x (this_beaming, SCM_EOL);
290 if (scm_ilength (gh_cdr (this_beaming)) > 0)
292 last_beaming = this_beaming;
298 MAKE_SCHEME_CALLBACK (Beam, brew_molecule, 1);
300 Beam::brew_molecule (SCM grob)
302 Grob *me = unsmob_grob (grob);
305 Link_array<Grob> stems=
306 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
307 Grob* xcommon = common_refpoint_of_array (stems, me, X_AXIS);
310 if (visible_stem_count (me))
312 // ugh -> use commonx
313 x0 = first_visible_stem (me)->relative_coordinate (xcommon, X_AXIS);
314 dx = last_visible_stem (me)->relative_coordinate (xcommon, X_AXIS) - x0;
318 x0 = stems[0]->relative_coordinate (xcommon, X_AXIS);
319 dx = stems.top ()->relative_coordinate (xcommon, X_AXIS) - x0;
322 SCM posns = me->get_grob_property ("positions");
324 if (!is_number_pair (posns))
326 programming_error ("No beam posns");
327 pos = Interval (0,0);
330 pos= ly_scm2interval (posns);
332 Real dy = pos.delta ();
333 Real dydx = (dy && dx) ? dy/dx : 0;
335 Real thick = get_thickness (me);
336 Real bdy = get_beam_translation (me);
338 SCM last_beaming = SCM_EOL;;
339 Real last_xposn = -1;
340 Real last_stem_width = -1 ;
342 Real gap_length =0.0;
343 SCM scm_gap = me->get_grob_property ("gap");
344 if (gh_number_p (scm_gap))
345 gap_length = gh_scm2double (scm_gap);
348 Real lt = me->get_paper ()->get_realvar (ly_symbol2scm ("linethickness"));
350 for (int i = 0; i<= stems.size(); i++)
352 Grob * st = (i < stems.size()) ? stems[i] : 0;
354 SCM this_beaming = st ? st->get_grob_property ("beaming") : SCM_EOL;
355 Real xposn = st ? st->relative_coordinate (xcommon, X_AXIS) : 0.0;
356 Real stem_width = st ? gh_scm2double (st->get_grob_property ("thickness")) *lt : 0 ;
357 Direction stem_dir = st ? to_dir (st->get_grob_property ("direction")) : CENTER;
359 We do the space left of ST, with lfliebertjes pointing to the
360 right from the left stem, and rfliebertjes pointing left from
363 SCM left = (i>0) ? gh_cdr (last_beaming) : SCM_EOL;
364 SCM right = st ? gh_car (this_beaming) : SCM_EOL;
366 Array<int> full_beams;
367 Array<int> lfliebertjes;
368 Array<int> rfliebertjes;
371 gh_pair_p (s); s =gh_cdr (s))
373 int b = gh_scm2int (gh_car (s));
374 if (scm_memq (gh_car(s), right) != SCM_BOOL_F)
380 lfliebertjes.push (b);
384 gh_pair_p (s); s =gh_cdr (s))
386 int b = gh_scm2int (gh_car (s));
387 if (scm_memq (gh_car(s), left) == SCM_BOOL_F)
389 rfliebertjes.push (b);
394 how much to stick out for beams across linebreaks
396 Real break_overshoot = 3.0;
397 Real w = (i > 0 && st) ? xposn - last_xposn : break_overshoot;
399 Real stem_offset =0.0;
402 w += last_stem_width / 2;
403 stem_offset = -last_stem_width / 2;
410 Real blot = me->get_paper ()->get_realvar (ly_symbol2scm ("blotdiameter"));
411 Molecule whole = Lookup::beam (dydx, w, thick, blot);
415 if (gh_number_p (me->get_grob_property ("gap-count")))
417 gap_count = gh_scm2int (me->get_grob_property ("gap-count"));
418 gapped = Lookup::beam (dydx, w - 2 * gap_length, thick, blot);
420 full_beams.sort (default_compare);
422 full_beams.reverse ();
426 for (int j = full_beams.size (); j--;)
433 b.translate_axis (gap_length, X_AXIS);
435 b.translate_axis (last_xposn - x0 + stem_offset, X_AXIS);
436 b.translate_axis (dydx * (last_xposn - x0) + bdy * full_beams[j], Y_AXIS);
438 the_beam.add_molecule (b);
443 if (lfliebertjes.size() || rfliebertjes.size())
449 int t = Stem::duration_log (st);
451 SCM proc = me->get_grob_property ("flag-width-function");
452 SCM result = gh_call1 (proc, scm_int2num (t));
453 nw_f = gh_scm2double (result);
456 nw_f = break_overshoot;
458 /* Half beam should be one note-width,
459 but let's make sure two half-beams never touch */
460 Real w = (i>0 && st) ? (xposn - last_xposn) : break_overshoot;
463 Molecule half = Lookup::beam (dydx, w, thick, blot);
464 for (int j = lfliebertjes.size(); j--;)
467 b.translate_axis (last_xposn - x0, X_AXIS);
468 b.translate_axis (dydx * (last_xposn-x0) + bdy * lfliebertjes[j], Y_AXIS);
469 the_beam.add_molecule (b);
471 for (int j = rfliebertjes.size(); j--;)
474 b.translate_axis (xposn - x0 - w , X_AXIS);
475 b.translate_axis (dydx * (xposn-x0 -w) + bdy * rfliebertjes[j], Y_AXIS);
476 the_beam.add_molecule (b);
482 last_stem_width = stem_width;
483 last_beaming = this_beaming;
486 the_beam.translate_axis (x0 - me->relative_coordinate (xcommon, X_AXIS), X_AXIS);
487 the_beam.translate_axis (pos[LEFT], Y_AXIS);
490 SCM quant_score = me->get_grob_property ("quant-score");
491 if (debug_beam_quanting_flag
492 && gh_string_p (quant_score))
496 This code prints the demerits for each beam. Perhaps this
497 should be switchable for those who want to twiddle with the
501 SCM properties = Font_interface::font_alist_chain (me);
503 Molecule tm = *unsmob_molecule (Text_item::interpret_markup
504 (me->get_paper ()->self_scm (), properties, quant_score));
505 the_beam.add_at_edge (Y_AXIS, UP, tm, 5.0, 0);
511 return the_beam.smobbed_copy();
518 Beam::get_default_dir (Grob *me)
520 Drul_array<int> total;
521 total[UP] = total[DOWN] = 0;
522 Drul_array<int> count;
523 count[UP] = count[DOWN] = 0;
526 Link_array<Grob> stems=
527 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
529 for (int i=0; i <stems.size (); i++)
532 Direction sd = get_grob_direction (s);
534 int center_distance = int(- d * Stem::head_positions (s) [-d]) >? 0;
535 int current = sd ? (1 + d * sd)/2 : center_distance;
542 } while (flip (&d) != DOWN);
544 SCM func = me->get_grob_property ("dir-function");
545 SCM s = gh_call2 (func,
546 gh_cons (scm_int2num (count[UP]),
547 scm_int2num (count[DOWN])),
548 gh_cons (scm_int2num (total[UP]),
549 scm_int2num (total[DOWN])));
551 if (gh_number_p (s) && gh_scm2int (s))
554 /* If dir is not determined: get default */
555 return to_dir (me->get_grob_property ("neutral-direction"));
559 /* Set all stems with non-forced direction to beam direction.
560 Urg: non-forced should become `without/with unforced' direction,
561 once stem gets cleaned-up. */
563 Beam::set_stem_directions (Grob *me, Direction d)
565 Link_array<Grob> stems
566 =Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
568 for (int i=0; i <stems.size (); i++)
572 SCM forcedir = s->get_grob_property ("direction");
573 if (!to_dir (forcedir))
574 set_grob_direction (s, d);
579 A union of intervals in the real line.
581 Abysmal performance (quadratic) for large N, hopefully we don't have
582 that large N. In any case, this should probably be rewritten to use
587 Array<Interval> allowed_regions_;
596 allowed_regions_.clear();
599 allowed_regions_.push (s);
602 void remove_interval (Interval rm)
604 for (int i = 0; i < allowed_regions_.size(); )
608 s.intersect (allowed_regions_[i]);
612 Interval before = allowed_regions_[i];
613 Interval after = allowed_regions_[i];
615 before[RIGHT] = s[LEFT];
616 after[LEFT] = s[RIGHT];
618 if (!before.is_empty () && before.length () > 0.0)
620 allowed_regions_.insert (before, i);
623 allowed_regions_.del (i);
624 if (!after.is_empty () && after.length () > 0.0)
626 allowed_regions_.insert (after, i);
638 Only try horizontal beams for knees. No reliable detection of
639 anything else is possible here, since we don't know funky-beaming
640 settings, or X-distances (slopes!) People that want sloped
641 knee-beams, should set the directions manually.
644 Beam::consider_auto_knees (Grob* me)
646 SCM scm = me->get_grob_property ("auto-knee-gap");
647 if (!gh_number_p (scm))
650 Real threshold = gh_scm2double (scm);
656 Link_array<Grob> stems=
657 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
659 Grob *common = common_refpoint_of_array (stems, me, Y_AXIS);
660 Real staff_space = Staff_symbol_referencer::staff_space (me);
662 Array<Interval> hps_array;
663 for (int i=0; i < stems.size (); i++)
665 Grob* stem = stems[i];
666 if (Stem::invisible_b (stem))
669 Interval hps = Stem::head_positions (stem);
674 hps *= staff_space * 0.5 ;
677 We could subtract beam Y position, but this routine only
678 sets stem directions, a constant shift does not have an
682 hps += stem->relative_coordinate (common, Y_AXIS);
684 if (to_dir (stem->get_grob_property ("direction")))
686 Direction stemdir = to_dir (stem->get_grob_property ("direction"));
687 hps[-stemdir] = - stemdir * infinity_f;
690 hps_array.push (hps);
692 gaps.remove_interval (hps);
696 Real max_gap_len =0.0;
698 for (int i = gaps.allowed_regions_.size() -1; i >= 0 ; i--)
700 Interval gap = gaps.allowed_regions_[i];
703 the outer gaps are not knees.
705 if (isinf (gap[LEFT]) || isinf(gap[RIGHT]))
708 if (gap.length () >= max_gap_len)
710 max_gap_len = gap.length();
715 if (max_gap_len > threshold)
718 for (int i = 0; i < stems.size(); i++)
720 Grob* stem = stems[i];
721 if (Stem::invisible_b (stem))
724 Interval hps = hps_array[j++];
727 Direction d = (hps.center () < max_gap.center()) ?
730 stem->set_grob_property ("direction", scm_int2num (d));
732 hps.intersect (max_gap);
733 assert (hps.is_empty () || hps.length () < 1e-6 );
740 /* Set stem's shorten property if unset.
743 take some y-position (chord/beam/nearest?) into account
744 scmify forced-fraction
746 This is done in beam because the shorten has to be uniform over the
751 Beam::set_stem_shorten (Grob *me)
754 shortening looks silly for x staff beams
759 Real forced_fraction = 1.0 * forced_stem_count (me)
760 / visible_stem_count (me);
762 int beam_count = get_beam_count (me);
764 SCM shorten_list = me->get_grob_property ("beamed-stem-shorten");
765 if (shorten_list == SCM_EOL)
768 Real staff_space = Staff_symbol_referencer::staff_space (me);
771 robust_list_ref (beam_count -1, shorten_list);
772 Real shorten_f = gh_scm2double (shorten_elt) * staff_space;
774 /* your similar cute comment here */
775 shorten_f *= forced_fraction;
778 me->set_grob_property ("shorten", gh_double2scm (shorten_f));
781 /* Call list of y-dy-callbacks, that handle setting of
785 MAKE_SCHEME_CALLBACK (Beam, after_line_breaking, 1);
787 Beam::after_line_breaking (SCM smob)
789 Grob *me = unsmob_grob (smob);
792 return SCM_UNSPECIFIED;
796 Beam::position_beam (Grob *me)
798 if (to_boolean (me->get_grob_property ("positioning-done")))
801 me->set_grob_property ("positioning-done", SCM_BOOL_T);
803 /* Copy to mutable list. */
804 SCM s = ly_deep_copy (me->get_grob_property ("positions"));
805 me->set_grob_property ("positions", s);
807 if (ly_car (s) == SCM_BOOL_F)
809 // one wonders if such genericity is necessary --hwn.
810 SCM callbacks = me->get_grob_property ("position-callbacks");
811 for (SCM i = callbacks; gh_pair_p (i); i = ly_cdr (i))
812 gh_call1 (ly_car (i), me->self_scm ());
815 set_stem_lengths (me);
820 Compute a first approximation to the beam slope.
822 MAKE_SCHEME_CALLBACK (Beam, least_squares, 1);
824 Beam::least_squares (SCM smob)
826 Grob *me = unsmob_grob (smob);
828 int count = visible_stem_count (me);
833 me->set_grob_property ("positions", ly_interval2scm (pos));
834 return SCM_UNSPECIFIED;
838 Array<Real> x_posns ;
839 Link_array<Grob> stems=
840 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
841 Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
842 Grob *commony = common_refpoint_of_array (stems, me, Y_AXIS);
844 Real my_y = me->relative_coordinate (commony, Y_AXIS);
846 Grob *fvs = first_visible_stem (me);
847 Grob *lvs = last_visible_stem (me);
849 Interval ideal (Stem::get_stem_info (fvs).ideal_y_
850 + fvs->relative_coordinate (commony, Y_AXIS) -my_y,
851 Stem::get_stem_info (lvs).ideal_y_
852 + lvs->relative_coordinate (commony, Y_AXIS) - my_y);
854 Real x0 = first_visible_stem (me)->relative_coordinate (commonx, X_AXIS);
855 for (int i=0; i < stems.size (); i++)
859 Real x = s->relative_coordinate (commonx, X_AXIS) - x0;
862 Real dx = last_visible_stem (me)->relative_coordinate (commonx, X_AXIS) - x0;
871 Interval chord (Stem::chord_start_y (first_visible_stem (me)),
872 Stem::chord_start_y (last_visible_stem (me)));
874 /* Simple beams (2 stems) on middle line should be allowed to be
877 However, if both stems reach middle line,
878 ideal[LEFT] == ideal[RIGHT] and ideal.delta () == 0.
880 For that case, we apply artificial slope */
881 if (!ideal[LEFT] && chord.delta () && count == 2)
884 Direction d = (Direction) (sign (chord.delta ()) * UP);
885 pos[d] = gh_scm2double (me->get_grob_property ("thickness")) / 2;
894 For broken beams this doesn't work well. In this case, the
895 slope esp. of the first part of a broken beam should predict
896 where the second part goes.
908 Array<Offset> ideals;
909 for (int i=0; i < stems.size (); i++)
912 if (Stem::invisible_b (s))
914 ideals.push (Offset (x_posns[i],
915 Stem::get_stem_info (s).ideal_y_
916 + s->relative_coordinate (commony, Y_AXIS)
920 minimise_least_squares (&dydx, &y, ideals);
923 me->set_grob_property ("least-squares-dy", gh_double2scm (dy));
924 pos = Interval (y, (y+dy));
927 me->set_grob_property ("positions", ly_interval2scm (pos));
929 return SCM_UNSPECIFIED;
934 We can't combine with previous function, since check concave and
935 slope damping comes first.
937 TODO: we should use the concaveness to control the amount of damping
941 MAKE_SCHEME_CALLBACK (Beam, shift_region_to_valid, 1);
943 Beam::shift_region_to_valid (SCM grob)
945 Grob *me = unsmob_grob (grob);
949 Array<Real> x_posns ;
950 Link_array<Grob> stems=
951 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
952 Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
953 Grob *commony = common_refpoint_of_array (stems, me, Y_AXIS);
955 Grob *fvs = first_visible_stem (me);
958 return SCM_UNSPECIFIED;
960 Real x0 =fvs->relative_coordinate (commonx, X_AXIS);
961 for (int i=0; i < stems.size (); i++)
965 Real x = s->relative_coordinate (commonx, X_AXIS) - x0;
969 Grob *lvs = last_visible_stem (me);
971 return SCM_UNSPECIFIED;
973 Real dx = lvs->relative_coordinate (commonx, X_AXIS) - x0;
975 Interval pos = ly_scm2interval ( me->get_grob_property ("positions"));
976 Real dy = pos.delta();
982 Shift the positions so that we have a chance of finding good
983 quants (i.e. no short stem failures.)
985 Interval feasible_left_point;
986 feasible_left_point.set_full ();
987 for (int i=0; i < stems.size (); i++)
990 if (Stem::invisible_b (s))
993 Direction d = Stem::get_direction (s);
996 Stem::get_stem_info (s).shortest_y_
997 - dydx * x_posns [i];
1000 left_y is now relative to the stem S. We want relative to
1001 ourselves, so translate:
1004 + s->relative_coordinate (commony, Y_AXIS)
1005 - me->relative_coordinate (commony, Y_AXIS);
1011 feasible_left_point.intersect (flp);
1014 if (feasible_left_point.is_empty ())
1016 warning (_("Not sure that we can find a nice beam slope (no viable initial configuration found)."));
1018 else if (!feasible_left_point.contains (y))
1020 if (isinf (feasible_left_point[DOWN]))
1021 y = feasible_left_point[UP] - REGION_SIZE;
1022 else if (isinf (feasible_left_point[UP]))
1023 y = feasible_left_point[DOWN]+ REGION_SIZE;
1025 y = feasible_left_point.center ();
1027 pos = Interval (y, (y+dy));
1028 me->set_grob_property ("positions", ly_interval2scm (pos));
1029 return SCM_UNSPECIFIED;
1033 MAKE_SCHEME_CALLBACK (Beam, check_concave, 1);
1035 Beam::check_concave (SCM smob)
1037 Grob *me = unsmob_grob (smob);
1039 Link_array<Grob> stems =
1040 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1042 for (int i = 0; i < stems.size ();)
1044 if (Stem::invisible_b (stems[i]))
1050 if (stems.size () < 3)
1051 return SCM_UNSPECIFIED;
1054 /* Concaveness #1: If distance of an inner notehead to line between
1055 two outer noteheads is bigger than CONCAVENESS-GAP (2.0ss),
1056 beam is concave (Heinz Stolba).
1058 In the case of knees, the line connecting outer heads is often
1059 not related to the beam slope (it may even go in the other
1060 direction). Skip the check when the outer stems point in
1061 different directions. --hwn
1064 bool concaveness1 = false;
1065 SCM gap = me->get_grob_property ("concaveness-gap");
1066 if (gh_number_p (gap)
1067 && Stem::get_direction(stems.top ())
1068 == Stem::get_direction(stems[0]))
1070 Real r1 = gh_scm2double (gap);
1071 Real dy = Stem::chord_start_y (stems.top ())
1072 - Stem::chord_start_y (stems[0]);
1075 Real slope = dy / (stems.size () - 1);
1077 Real y0 = Stem::chord_start_y (stems[0]);
1078 for (int i = 1; i < stems.size () - 1; i++)
1080 Real c = (Stem::chord_start_y (stems[i]) - y0) - i * slope;
1083 concaveness1 = true;
1090 /* Concaveness #2: Sum distances of inner noteheads that fall
1091 outside the interval of the two outer noteheads.
1093 We only do this for beams where first and last stem have the same
1097 Note that "convex" stems compensate for "concave" stems.
1098 (is that intentional?) --hwn.
1101 Real concaveness2 = 0;
1102 SCM thresh = me->get_grob_property ("concaveness-threshold");
1103 Real r2 = infinity_f;
1104 if (!concaveness1 && gh_number_p (thresh)
1105 && Stem::get_direction(stems.top ())
1106 == Stem::get_direction(stems[0]))
1108 r2 = gh_scm2double (thresh);
1110 Direction dir = Stem::get_direction(stems.top ());
1112 Interval iv (Stem::chord_start_y (stems[0]),
1113 Stem::chord_start_y (stems.top ()));
1115 if (iv[MAX] < iv[MIN])
1118 for (int i = 1; i < stems.size () - 1; i++)
1120 Real f = Stem::chord_start_y (stems[i]);
1121 concave += ((f - iv[MAX] ) >? 0) +
1122 ((f - iv[MIN] ) <? 0);
1125 concaveness2 = concave / (stems.size () - 2);
1129 ugh: this is the a kludge to get
1130 input/regression/beam-concave.ly to behave as
1136 huh? we're dividing twice (which is not scalable) meaning that
1137 the longer the beam, the more unlikely it will be
1138 concave. Maybe you would even expect the other way around??
1143 concaveness2 /= (stems.size () - 2);
1146 /* TODO: some sort of damping iso -> plain horizontal */
1147 if (concaveness1 || concaveness2 > r2)
1149 Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
1150 Real r = pos.linear_combination (0);
1151 me->set_grob_property ("positions", ly_interval2scm (Interval (r, r)));
1152 me->set_grob_property ("least-squares-dy", gh_double2scm (0));
1155 return SCM_UNSPECIFIED;
1158 /* This neat trick is by Werner Lemberg,
1159 damped = tanh (slope)
1160 corresponds with some tables in [Wanske] CHECKME */
1161 MAKE_SCHEME_CALLBACK (Beam, slope_damping, 1);
1163 Beam::slope_damping (SCM smob)
1165 Grob *me = unsmob_grob (smob);
1167 if (visible_stem_count (me) <= 1)
1168 return SCM_UNSPECIFIED;
1170 SCM s = me->get_grob_property ("damping");
1171 int damping = gh_scm2int (s);
1175 Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
1176 Real dy = pos.delta ();
1178 Grob *fvs = first_visible_stem (me);
1179 Grob *lvs = last_visible_stem (me);
1181 Grob *commonx = fvs->common_refpoint (lvs, X_AXIS);
1184 Real dx = last_visible_stem (me)->relative_coordinate (commonx, X_AXIS)
1185 - first_visible_stem (me)->relative_coordinate (commonx, X_AXIS);
1186 Real dydx = dy && dx ? dy/dx : 0;
1187 dydx = 0.6 * tanh (dydx) / damping;
1189 Real damped_dy = dydx * dx;
1190 pos[LEFT] += (dy - damped_dy) / 2;
1191 pos[RIGHT] -= (dy - damped_dy) / 2;
1193 me->set_grob_property ("positions", ly_interval2scm (pos));
1195 return SCM_UNSPECIFIED;
1199 Report slice containing the numbers that are both in (car BEAMING)
1203 where_are_the_whole_beams(SCM beaming)
1207 for( SCM s = gh_car (beaming); gh_pair_p (s) ; s = gh_cdr (s))
1209 if (scm_memq (gh_car (s), gh_cdr (beaming)) != SCM_BOOL_F)
1211 l.add_point (gh_scm2int (gh_car (s)));
1217 /* Return the Y position of the stem-end, given the Y-left, Y-right
1218 in POS for stem S. This Y position is relative to S. */
1220 Beam::calc_stem_y (Grob *me, Grob* s, Grob ** common,
1222 Interval pos, bool french)
1224 Real beam_translation = get_beam_translation (me);
1227 Real r = s->relative_coordinate (common[X_AXIS], X_AXIS) - xl;
1228 Real dy = pos.delta ();
1230 Real stem_y_beam0 = (dy && dx
1235 Direction my_dir = get_grob_direction (s);
1236 SCM beaming = s->get_grob_property ("beaming");
1238 Real stem_y = stem_y_beam0;
1241 Slice bm = where_are_the_whole_beams (beaming);
1242 if (!bm.is_empty ())
1243 stem_y += beam_translation * bm[-my_dir];
1247 Slice bm = Stem::beam_multiplicity(s);
1248 if (!bm.is_empty ())
1249 stem_y +=bm[my_dir] * beam_translation;
1252 Real id = me->relative_coordinate (common[Y_AXIS], Y_AXIS)
1253 - s->relative_coordinate (common[Y_AXIS], Y_AXIS);
1259 Hmm. At this time, beam position and slope are determined. Maybe,
1260 stem directions and length should set to relative to the chord's
1261 position of the beam. */
1263 Beam::set_stem_lengths (Grob *me)
1265 Link_array<Grob> stems=
1266 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
1272 for (int a = 2; a--;)
1273 common[a] = common_refpoint_of_array (stems, me, Axis(a));
1275 Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
1276 Real staff_space = Staff_symbol_referencer::staff_space (me);
1280 if (gh_number_p (me->get_grob_property ("gap-count"))
1281 &&gh_scm2int (me->get_grob_property ("gap-count")))
1284 thick = get_thickness(me);
1287 // ugh -> use commonx
1288 Grob * fvs = first_visible_stem (me);
1289 Grob *lvs = last_visible_stem (me);
1291 Real xl = fvs ? fvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
1292 Real xr = lvs ? lvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
1294 for (int i=0; i < stems.size (); i++)
1297 if (Stem::invisible_b (s))
1300 bool french = to_boolean (s->get_grob_property ("french-beaming"));
1301 Real stem_y = calc_stem_y (me, s, common,
1303 pos, french && s != lvs && s!= fvs);
1306 Make the stems go up to the end of the beam. This doesn't matter
1307 for normal beams, but for tremolo beams it looks silly otherwise.
1310 stem_y += thick * 0.5 * get_grob_direction (s);
1312 Stem::set_stemend (s, 2* stem_y / staff_space);
1317 Beam::set_beaming (Grob *me, Beaming_info_list *beaming)
1319 Link_array<Grob> stems=
1320 Pointer_group_interface__extract_grobs (me, (Grob *)0, "stems");
1323 for (int i=0; i < stems.size (); i++)
1326 Don't overwrite user settings.
1331 /* Don't set beaming for outside of outer stems */
1332 if ((d == LEFT && i == 0)
1333 ||(d == RIGHT && i == stems.size () -1))
1336 Grob *st = stems[i];
1337 SCM beaming_prop = st->get_grob_property ("beaming");
1338 if (beaming_prop == SCM_EOL ||
1339 index_get_cell (beaming_prop, d) == SCM_EOL)
1341 int b = beaming->infos_.elem (i).beams_i_drul_[d];
1343 && i < stems.size() -1
1344 && Stem::invisible_b (st))
1345 b = b <? beaming->infos_.elem(i).beams_i_drul_[-d];
1347 Stem::set_beaming (st, b, d);
1350 while (flip (&d) != LEFT);
1355 Beam::forced_stem_count (Grob *me)
1357 Link_array<Grob>stems =
1358 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1360 for (int i=0; i < stems.size (); i++)
1364 if (Stem::invisible_b (s))
1367 /* I can imagine counting those boundaries as a half forced stem,
1368 but let's count them full for now. */
1369 if (abs (Stem::chord_start_y (s)) > 0.1
1370 && (Stem::get_direction (s) != Stem::get_default_dir (s)))
1380 Beam::visible_stem_count (Grob *me)
1382 Link_array<Grob>stems =
1383 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1385 for (int i = stems.size (); i--;)
1387 if (!Stem::invisible_b (stems[i]))
1394 Beam::first_visible_stem (Grob *me)
1396 Link_array<Grob>stems =
1397 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1399 for (int i = 0; i < stems.size (); i++)
1401 if (!Stem::invisible_b (stems[i]))
1408 Beam::last_visible_stem (Grob *me)
1410 Link_array<Grob>stems =
1411 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1412 for (int i = stems.size (); i--;)
1414 if (!Stem::invisible_b (stems[i]))
1424 handle rest under beam (do_post: beams are calculated now)
1425 what about combination of collisions and rest under beam.
1429 rest -> stem -> beam -> interpolate_y_position ()
1431 MAKE_SCHEME_CALLBACK (Beam, rest_collision_callback, 2);
1433 Beam::rest_collision_callback (SCM element_smob, SCM axis)
1435 Grob *rest = unsmob_grob (element_smob);
1436 Axis a = (Axis) gh_scm2int (axis);
1438 if (gh_number_p (rest->get_grob_property ("staff-position")))
1439 return gh_int2scm (0);
1441 assert (a == Y_AXIS);
1443 Grob *st = unsmob_grob (rest->get_grob_property ("stem"));
1446 return gh_double2scm (0.0);
1447 Grob *beam = unsmob_grob (stem->get_grob_property ("beam"));
1449 || !Beam::has_interface (beam)
1450 || !Beam::visible_stem_count (beam))
1451 return gh_double2scm (0.0);
1453 Interval pos (0, 0);
1454 SCM s = beam->get_grob_property ("positions");
1455 if (gh_pair_p (s) && gh_number_p (ly_car (s)))
1456 pos = ly_scm2interval (s);
1458 Real dy = pos.delta ();
1459 // ugh -> use commonx
1460 Real x0 = first_visible_stem (beam)->relative_coordinate (0, X_AXIS);
1461 Real dx = last_visible_stem (beam)->relative_coordinate (0, X_AXIS) - x0;
1462 Real dydx = dy && dx ? dy/dx : 0;
1464 Direction d = Stem::get_direction (stem);
1465 Real stem_y = (pos[LEFT]
1466 + (stem->relative_coordinate (0, X_AXIS) - x0) * dydx)
1469 Real beam_translation = get_beam_translation (beam);
1470 Real beam_thickness = gh_scm2double (beam->get_grob_property ("thickness"));
1471 int beam_count = get_direction_beam_count (beam, d);
1472 Real height_of_my_beams = beam_thickness
1473 + (beam_count - 1) * beam_translation;
1474 Real beam_y = stem_y - height_of_my_beams + beam_thickness / 2.0;
1476 Real staff_space = Staff_symbol_referencer::staff_space (rest);
1478 /* Better calculate relative-distance directly, rather than using
1480 Grob *common_x = rest->common_refpoint (beam, Y_AXIS);
1481 Real rest_dim = rest->extent (common_x, Y_AXIS)[d] / staff_space * d;
1483 Real minimum_distance = gh_scm2double
1484 (rest->get_grob_property ("minimum-beam-collision-distance"));
1486 Real distance = beam_y - rest_dim;
1489 shift = minimum_distance - distance;
1490 else if (minimum_distance > distance)
1491 shift = minimum_distance - distance;
1493 int stafflines = Staff_symbol_referencer::line_count (rest);
1495 /* Always move discretely by half spaces */
1496 Real discrete_shift = ceil (shift * 2.0) / 2.0;
1498 /* Inside staff, move by whole spaces*/
1499 if ((rest->extent (common_x, Y_AXIS)[d] + discrete_shift) * d
1501 ||(rest->extent (common_x, Y_AXIS)[-d] + discrete_shift) * -d
1503 discrete_shift = ceil (discrete_shift);
1505 return gh_double2scm (-d * discrete_shift);
1509 Beam::knee_b (Grob* me)
1511 SCM k = me->get_grob_property ("knee");
1512 if (gh_boolean_p (k))
1513 return gh_scm2bool (k);
1517 for (SCM s = me->get_grob_property ("stems"); gh_pair_p (s); s = ly_cdr (s))
1519 Direction dir = get_grob_direction (unsmob_grob (ly_car (s)));
1528 me->set_grob_property ("knee", gh_bool2scm (knee));
1534 Beam::get_direction_beam_count (Grob *me, Direction d )
1536 Link_array<Grob>stems =
1537 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1540 for (int i = stems.size (); i--;)
1543 Should we take invisible stems into account?
1545 if (Stem::get_direction (stems[i]) == d)
1546 bc = bc >? (Stem::beam_multiplicity (stems[i]).length () + 1);
1553 ADD_INTERFACE (Beam, "beam-interface",
1556 "#'thickness= weight of beams, in staffspace "
1559 "We take the least squares line through the ideal-length stems, and "
1560 "then damp that using "
1562 " damped = tanh (slope) \n"
1564 "this gives an unquantized left and right position for the beam end. "
1565 "Then we take all combinations of quantings near these left and right "
1566 "positions, and give them a score (according to how close they are to "
1567 "the ideal slope, how close the result is to the ideal stems, etc.). We "
1568 "take the best scoring combination. "
1570 "knee positioning-done position-callbacks concaveness-gap concaveness-threshold dir-function quant-score auto-knee-gap gap gap-count chord-tremolo beamed-stem-shorten shorten least-squares-dy damping flag-width-function neutral-direction positions space-function thickness");