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"
47 #define DEBUG_QUANTING 1
49 bool debug_beam_quanting_flag;
53 #include "text-item.hh" // debug output.
54 #include "font-interface.hh" // debug output.
59 Beam::add_stem (Grob *me, Grob *s)
61 Pointer_group_interface::add_grob (me, ly_symbol2scm ("stems"), s);
63 s->add_dependency (me);
65 assert (!Stem::get_beam (s));
66 s->set_grob_property ("beam", me->self_scm ());
68 add_bound_item (dynamic_cast<Spanner*> (me), dynamic_cast<Item*> (s));
73 Beam::get_thickness (Grob * me)
75 SCM th = me->get_grob_property ("thickness");
77 return gh_scm2double (th)* Staff_symbol_referencer::staff_space (me);
82 /* Return the translation between 2 adjoining beams. */
84 Beam::get_beam_translation (Grob *me)
86 SCM func = me->get_grob_property ("space-function");
87 SCM s = gh_call2 (func, me->self_scm (), scm_int2num (get_beam_count (me)));
88 return gh_scm2double (s);
91 /* Maximum beam_count. */
93 Beam::get_beam_count (Grob *me)
96 for (SCM s = me->get_grob_property ("stems"); gh_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 = me->get_paper ()->get_realvar (ly_symbol2scm ("linethickness"));
116 Real thickness = get_thickness (me);
118 Real beam_translation = gh_scm2int (beam_count) < 4
119 ? (2*staff_space + line - thickness) / 2.0
120 : (3*staff_space + line - thickness) / 3.0;
122 return gh_double2scm (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_grob_property ("stems");
152 if (scm_ilength (stems) == 1)
154 me->warning (_ ("Beam has less than two stems. Removing beam."));
156 unsmob_grob (gh_car (stems))->set_grob_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 (gh_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 = gh_car (right_beaming); gh_pair_p (s); s = gh_cdr (s))
212 int k = - right_dir * gh_scm2int (gh_car (s)) + i;
213 if (scm_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_grob_property ("beaming");
242 Direction this_dir = get_grob_direction (this_stem);
243 if (gh_pair_p (last_beaming) && gh_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 (; gh_pair_p (s); s = gh_cdr (s))
261 start_point - this_dir * gh_scm2int (gh_car (s));
263 new_slice.add_point (new_beam_pos);
264 gh_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 gh_set_car_x ( this_beaming, SCM_EOL);
277 SCM s = gh_cdr (this_beaming);
278 for (; gh_pair_p (s); s = gh_cdr (s))
280 int np = - this_dir * gh_scm2int (gh_car(s));
281 gh_set_car_x (s, scm_int2num (np));
282 last_int.add_point (np);
286 if (i == stems.size () -1)
288 gh_set_cdr_x (this_beaming, SCM_EOL);
291 if (scm_ilength (gh_cdr (this_beaming)) > 0)
293 last_beaming = this_beaming;
299 MAKE_SCHEME_CALLBACK (Beam, brew_molecule, 1);
301 Beam::brew_molecule (SCM grob)
303 Grob *me = unsmob_grob (grob);
304 Link_array<Grob> stems=
305 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
306 Grob* xcommon = common_refpoint_of_array (stems, me, X_AXIS);
309 if (visible_stem_count (me))
311 // ugh -> use commonx
312 x0 = first_visible_stem (me)->relative_coordinate (xcommon, X_AXIS);
313 dx = last_visible_stem (me)->relative_coordinate (xcommon, X_AXIS) - x0;
317 x0 = stems[0]->relative_coordinate (xcommon, X_AXIS);
318 dx = stems.top ()->relative_coordinate (xcommon, X_AXIS) - x0;
321 SCM posns = me->get_grob_property ("positions");
323 if (!is_number_pair (posns))
325 programming_error ("No beam posns");
326 pos = Interval (0,0);
329 pos= ly_scm2interval (posns);
331 Real dy = pos.delta ();
332 Real dydx = (dy && dx) ? dy/dx : 0;
334 Real thick = get_thickness (me);
335 Real bdy = get_beam_translation (me);
337 SCM last_beaming = SCM_EOL;;
338 Real last_xposn = -1;
339 Real last_width = -1 ;
341 Real gap_length =0.0;
342 SCM scm_gap = me->get_grob_property ("gap");
343 if (gh_number_p (scm_gap))
344 gap_length = gh_scm2double (scm_gap);
347 Real lt = me->get_paper ()->get_realvar (ly_symbol2scm ("linethickness"));
349 for (int i = 0; i<= stems.size(); i++)
351 Grob * st = (i < stems.size()) ? stems[i] : 0;
353 SCM this_beaming = st ? st->get_grob_property ("beaming") : SCM_EOL;
354 Real xposn = st ? st->relative_coordinate (xcommon, X_AXIS) : 0.0;
355 Real stem_width = st ? gh_scm2double (st->get_grob_property ("thickness")) *lt : 0 ;
356 Direction stem_dir = st ? to_dir (st->get_grob_property ("direction")) : CENTER;
358 We do the space left of ST, with lfliebertjes pointing to the
359 right from the left stem, and rfliebertjes pointing left from
362 SCM left = (i>0) ? gh_cdr (last_beaming) : SCM_EOL;
363 SCM right = st ? gh_car (this_beaming) : SCM_EOL;
365 Array<int> full_beams;
366 Array<int> lfliebertjes;
367 Array<int> rfliebertjes;
370 gh_pair_p (s); s =gh_cdr (s))
372 int b = gh_scm2int (gh_car (s));
373 if (scm_memq (gh_car(s), right) != SCM_BOOL_F)
379 lfliebertjes.push (b);
383 gh_pair_p (s); s =gh_cdr (s))
385 int b = gh_scm2int (gh_car (s));
386 if (scm_memq (gh_car(s), left) == SCM_BOOL_F)
388 rfliebertjes.push (b);
393 how much to stick out for beams across linebreaks
395 Real break_overshoot = 3.0;
396 Real w = (i>0 && st)? xposn - last_xposn : break_overshoot;
397 Real stem_offset = 0.0;
398 Real width_corr = 0.0;
401 stem_offset -= last_width/2;
402 width_corr += last_width/2;
405 if (i == stems.size() -1)
407 width_corr += stem_width/2;
411 Molecule whole = Lookup::beam (dydx, w + width_corr, thick);
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 + width_corr - 2 * gap_length, thick);
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);
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_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 if (debug_beam_quanting_flag)
493 This code prints the demerits for each beam. Perhaps this
494 should be switchable for those who want to twiddle with the
498 SCM properties = Font_interface::font_alist_chain (me);
500 Molecule tm = *unsmob_molecule (Text_item::interpret_markup
501 (me->get_paper ()->self_scm (), properties, me->get_grob_property ("quant-score")));
502 the_beam.add_at_edge (Y_AXIS, UP, tm, 5.0, 0);
508 return the_beam.smobbed_copy();
515 Beam::get_default_dir (Grob *me)
517 Drul_array<int> total;
518 total[UP] = total[DOWN] = 0;
519 Drul_array<int> count;
520 count[UP] = count[DOWN] = 0;
523 Link_array<Grob> stems=
524 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
526 for (int i=0; i <stems.size (); i++)
529 Direction sd = get_grob_direction (s);
531 int center_distance = int(- d * Stem::head_positions (s) [-d]) >? 0;
532 int current = sd ? (1 + d * sd)/2 : center_distance;
539 } while (flip (&d) != DOWN);
541 SCM func = me->get_grob_property ("dir-function");
542 SCM s = gh_call2 (func,
543 gh_cons (scm_int2num (count[UP]),
544 scm_int2num (count[DOWN])),
545 gh_cons (scm_int2num (total[UP]),
546 scm_int2num (total[DOWN])));
548 if (gh_number_p (s) && gh_scm2int (s))
551 /* If dir is not determined: get default */
552 return to_dir (me->get_grob_property ("neutral-direction"));
556 /* Set all stems with non-forced direction to beam direction.
557 Urg: non-forced should become `without/with unforced' direction,
558 once stem gets cleaned-up. */
560 Beam::set_stem_directions (Grob *me, Direction d)
562 Link_array<Grob> stems
563 =Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
565 for (int i=0; i <stems.size (); i++)
569 SCM forcedir = s->get_grob_property ("direction");
570 if (!to_dir (forcedir))
571 set_grob_direction (s, d);
576 A union of intervals in the real line.
578 Abysmal performance (quadratic) for large N, hopefully we don't have
579 that large N. In any case, this should probably be rewritten to use
584 Array<Interval> allowed_regions_;
593 allowed_regions_.clear();
596 allowed_regions_.push (s);
599 void remove_interval (Interval rm)
601 for (int i = 0; i < allowed_regions_.size(); )
605 s.intersect (allowed_regions_[i]);
609 Interval before = allowed_regions_[i];
610 Interval after = allowed_regions_[i];
612 before[RIGHT] = s[LEFT];
613 after[LEFT] = s[RIGHT];
615 if (!before.is_empty () && before.length () > 0.0)
617 allowed_regions_.insert (before, i);
620 allowed_regions_.del (i);
621 if (!after.is_empty () && after.length () > 0.0)
623 allowed_regions_.insert (after, i);
635 Only try horizontal beams for knees. No reliable detection of
636 anything else is possible here, since we don't know funky-beaming
637 settings, or X-distances (slopes!) People that want sloped
638 knee-beams, should set the directions manually.
641 Beam::consider_auto_knees (Grob* me)
643 SCM scm = me->get_grob_property ("auto-knee-gap");
644 if (!gh_number_p (scm))
647 Real threshold = gh_scm2double (scm);
653 Link_array<Grob> stems=
654 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
656 Grob *common = common_refpoint_of_array (stems, me, Y_AXIS);
657 Real staff_space = Staff_symbol_referencer::staff_space (me);
659 Array<Interval> hps_array;
660 for (int i=0; i < stems.size (); i++)
662 Grob* stem = stems[i];
663 if (Stem::invisible_b (stem))
666 Interval hps = Stem::head_positions (stem);
671 hps *= staff_space * 0.5 ;
674 We could subtract beam Y position, but this routine only
675 sets stem directions, a constant shift does not have an
679 hps += stem->relative_coordinate (common, Y_AXIS);
681 if (to_dir (stem->get_grob_property ("direction")))
683 Direction stemdir = to_dir (stem->get_grob_property ("direction"));
684 hps[-stemdir] = - stemdir * infinity_f;
687 hps_array.push (hps);
689 gaps.remove_interval (hps);
693 Real max_gap_len =0.0;
695 for (int i = gaps.allowed_regions_.size() -1; i >= 0 ; i--)
697 Interval gap = gaps.allowed_regions_[i];
700 the outer gaps are not knees.
702 if (isinf (gap[LEFT]) || isinf(gap[RIGHT]))
705 if (gap.length () >= max_gap_len)
707 max_gap_len = gap.length();
712 if (max_gap_len > threshold)
715 for (int i = 0; i < stems.size(); i++)
717 Grob* stem = stems[i];
718 if (Stem::invisible_b (stem))
721 Interval hps = hps_array[j++];
724 Direction d = (hps.center () < max_gap.center()) ?
727 stem->set_grob_property ("direction", scm_int2num (d));
729 hps.intersect (max_gap);
730 assert (hps.is_empty () || hps.length () < 1e-6 );
737 /* Set stem's shorten property if unset.
740 take some y-position (chord/beam/nearest?) into account
741 scmify forced-fraction
743 This is done in beam because the shorten has to be uniform over the
748 Beam::set_stem_shorten (Grob *me)
751 shortening looks silly for x staff beams
756 Real forced_fraction = 1.0 * forced_stem_count (me)
757 / visible_stem_count (me);
759 int beam_count = get_beam_count (me);
761 SCM shorten_list = me->get_grob_property ("beamed-stem-shorten");
762 if (shorten_list == SCM_EOL)
765 Real staff_space = Staff_symbol_referencer::staff_space (me);
768 robust_list_ref (beam_count -1, shorten_list);
769 Real shorten_f = gh_scm2double (shorten_elt) * staff_space;
771 /* your similar cute comment here */
772 shorten_f *= forced_fraction;
775 me->set_grob_property ("shorten", gh_double2scm (shorten_f));
778 /* Call list of y-dy-callbacks, that handle setting of
782 MAKE_SCHEME_CALLBACK (Beam, after_line_breaking, 1);
784 Beam::after_line_breaking (SCM smob)
786 Grob *me = unsmob_grob (smob);
788 /* Copy to mutable list. */
789 SCM s = ly_deep_copy (me->get_grob_property ("positions"));
790 me->set_grob_property ("positions", s);
792 if (ly_car (s) == SCM_BOOL_F)
795 // one wonders if such genericity is necessary --hwn.
796 SCM callbacks = me->get_grob_property ("position-callbacks");
797 for (SCM i = callbacks; gh_pair_p (i); i = ly_cdr (i))
798 gh_call1 (ly_car (i), smob);
801 set_stem_lengths (me);
802 return SCM_UNSPECIFIED;
807 Compute a first approximation to the beam slope.
809 MAKE_SCHEME_CALLBACK (Beam, least_squares, 1);
811 Beam::least_squares (SCM smob)
813 Grob *me = unsmob_grob (smob);
815 int count = visible_stem_count (me);
820 me->set_grob_property ("positions", ly_interval2scm (pos));
821 return SCM_UNSPECIFIED;
825 Array<Real> x_posns ;
826 Link_array<Grob> stems=
827 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
828 Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
829 Grob *commony = common_refpoint_of_array (stems, me, Y_AXIS);
831 Real my_y = me->relative_coordinate (commony, Y_AXIS);
833 Grob *fvs = first_visible_stem (me);
834 Grob *lvs = last_visible_stem (me);
836 Interval ideal (Stem::get_stem_info (fvs).ideal_y_
837 + fvs->relative_coordinate (commony, Y_AXIS) -my_y,
838 Stem::get_stem_info (lvs).ideal_y_
839 + lvs->relative_coordinate (commony, Y_AXIS) - my_y);
841 Real x0 = first_visible_stem (me)->relative_coordinate (commonx, X_AXIS);
842 for (int i=0; i < stems.size (); i++)
846 Real x = s->relative_coordinate (commonx, X_AXIS) - x0;
849 Real dx = last_visible_stem (me)->relative_coordinate (commonx, X_AXIS) - x0;
858 Interval chord (Stem::chord_start_y (first_visible_stem (me)),
859 Stem::chord_start_y (last_visible_stem (me)));
861 /* Simple beams (2 stems) on middle line should be allowed to be
864 However, if both stems reach middle line,
865 ideal[LEFT] == ideal[RIGHT] and ideal.delta () == 0.
867 For that case, we apply artificial slope */
868 if (!ideal[LEFT] && chord.delta () && count == 2)
871 Direction d = (Direction) (sign (chord.delta ()) * UP);
872 pos[d] = gh_scm2double (me->get_grob_property ("thickness")) / 2;
881 For broken beams this doesn't work well. In this case, the
882 slope esp. of the first part of a broken beam should predict
883 where the second part goes.
895 Array<Offset> ideals;
896 for (int i=0; i < stems.size (); i++)
899 if (Stem::invisible_b (s))
901 ideals.push (Offset (x_posns[i],
902 Stem::get_stem_info (s).ideal_y_
903 + s->relative_coordinate (commony, Y_AXIS)
907 minimise_least_squares (&dydx, &y, ideals);
910 me->set_grob_property ("least-squares-dy", gh_double2scm (dy));
911 pos = Interval (y, (y+dy));
914 me->set_grob_property ("positions", ly_interval2scm (pos));
916 return SCM_UNSPECIFIED;
921 We can't combine with previous function, since check concave and
922 slope damping comes first.
924 TODO: we should use the concaveness to control the amount of damping
928 MAKE_SCHEME_CALLBACK (Beam, shift_region_to_valid, 1);
930 Beam::shift_region_to_valid (SCM grob)
932 Grob *me = unsmob_grob (grob);
936 Array<Real> x_posns ;
937 Link_array<Grob> stems=
938 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
939 Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
940 Grob *commony = common_refpoint_of_array (stems, me, Y_AXIS);
942 Grob *fvs = first_visible_stem (me);
945 return SCM_UNSPECIFIED;
947 Real x0 =fvs->relative_coordinate (commonx, X_AXIS);
948 for (int i=0; i < stems.size (); i++)
952 Real x = s->relative_coordinate (commonx, X_AXIS) - x0;
956 Grob *lvs = last_visible_stem (me);
958 return SCM_UNSPECIFIED;
960 Real dx = lvs->relative_coordinate (commonx, X_AXIS) - x0;
962 Interval pos = ly_scm2interval ( me->get_grob_property ("positions"));
963 Real dy = pos.delta();
969 Shift the positions so that we have a chance of finding good
970 quants (i.e. no short stem failures.)
972 Interval feasible_left_point;
973 feasible_left_point.set_full ();
974 for (int i=0; i < stems.size (); i++)
977 if (Stem::invisible_b (s))
980 Direction d = Stem::get_direction (s);
983 Stem::get_stem_info (s).shortest_y_
984 - dydx * x_posns [i];
987 left_y is now relative to the stem S. We want relative to
988 ourselves, so translate:
991 + s->relative_coordinate (commony, Y_AXIS)
992 - me->relative_coordinate (commony, Y_AXIS);
998 feasible_left_point.intersect (flp);
1001 if (feasible_left_point.is_empty ())
1003 warning (_("Not sure that we can find a nice beam slope (no viable initial configuration found)."));
1005 else if (!feasible_left_point.elem_b(y))
1007 if (isinf (feasible_left_point[DOWN]))
1008 y = feasible_left_point[UP] - REGION_SIZE;
1009 else if (isinf (feasible_left_point[UP]))
1010 y = feasible_left_point[DOWN]+ REGION_SIZE;
1012 y = feasible_left_point.center ();
1014 pos = Interval (y, (y+dy));
1015 me->set_grob_property ("positions", ly_interval2scm (pos));
1016 return SCM_UNSPECIFIED;
1020 MAKE_SCHEME_CALLBACK (Beam, check_concave, 1);
1022 Beam::check_concave (SCM smob)
1024 Grob *me = unsmob_grob (smob);
1026 Link_array<Grob> stems =
1027 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1029 for (int i = 0; i < stems.size ();)
1031 if (Stem::invisible_b (stems[i]))
1037 if (stems.size () < 3)
1038 return SCM_UNSPECIFIED;
1041 /* Concaveness #1: If distance of an inner notehead to line between
1042 two outer noteheads is bigger than CONCAVENESS-GAP (2.0ss),
1043 beam is concave (Heinz Stolba).
1045 In the case of knees, the line connecting outer heads is often
1046 not related to the beam slope (it may even go in the other
1047 direction). Skip the check when the outer stems point in
1048 different directions. --hwn
1051 bool concaveness1 = false;
1052 SCM gap = me->get_grob_property ("concaveness-gap");
1053 if (gh_number_p (gap)
1054 && Stem::get_direction(stems.top ())
1055 == Stem::get_direction(stems[0]))
1057 Real r1 = gh_scm2double (gap);
1058 Real dy = Stem::chord_start_y (stems.top ())
1059 - Stem::chord_start_y (stems[0]);
1062 Real slope = dy / (stems.size () - 1);
1064 Real y0 = Stem::chord_start_y (stems[0]);
1065 for (int i = 1; i < stems.size () - 1; i++)
1067 Real c = (Stem::chord_start_y (stems[i]) - y0) - i * slope;
1070 concaveness1 = true;
1077 /* Concaveness #2: Sum distances of inner noteheads that fall
1078 outside the interval of the two outer noteheads.
1080 We only do this for beams where first and last stem have the same
1084 Note that "convex" stems compensate for "concave" stems.
1085 (is that intentional?) --hwn.
1088 Real concaveness2 = 0;
1089 SCM thresh = me->get_grob_property ("concaveness-threshold");
1090 Real r2 = infinity_f;
1091 if (!concaveness1 && gh_number_p (thresh)
1092 && Stem::get_direction(stems.top ())
1093 == Stem::get_direction(stems[0]))
1095 r2 = gh_scm2double (thresh);
1097 Direction dir = Stem::get_direction(stems.top ());
1099 Interval iv (Stem::chord_start_y (stems[0]),
1100 Stem::chord_start_y (stems.top ()));
1102 if (iv[MAX] < iv[MIN])
1105 for (int i = 1; i < stems.size () - 1; i++)
1107 Real f = Stem::chord_start_y (stems[i]);
1108 concave += ((f - iv[MAX] ) >? 0) +
1109 ((f - iv[MIN] ) <? 0);
1112 concaveness2 = concave / (stems.size () - 2);
1116 ugh: this is the a kludge to get
1117 input/regression/beam-concave.ly to behave as
1123 huh? we're dividing twice (which is not scalable) meaning that
1124 the longer the beam, the more unlikely it will be
1125 concave. Maybe you would even expect the other way around??
1130 concaveness2 /= (stems.size () - 2);
1133 /* TODO: some sort of damping iso -> plain horizontal */
1134 if (concaveness1 || concaveness2 > r2)
1136 Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
1137 Real r = pos.linear_combination (0);
1138 me->set_grob_property ("positions", ly_interval2scm (Interval (r, r)));
1139 me->set_grob_property ("least-squares-dy", gh_double2scm (0));
1142 return SCM_UNSPECIFIED;
1145 /* This neat trick is by Werner Lemberg,
1146 damped = tanh (slope)
1147 corresponds with some tables in [Wanske] CHECKME */
1148 MAKE_SCHEME_CALLBACK (Beam, slope_damping, 1);
1150 Beam::slope_damping (SCM smob)
1152 Grob *me = unsmob_grob (smob);
1154 if (visible_stem_count (me) <= 1)
1155 return SCM_UNSPECIFIED;
1157 SCM s = me->get_grob_property ("damping");
1158 int damping = gh_scm2int (s);
1162 Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
1163 Real dy = pos.delta ();
1165 Grob *fvs = first_visible_stem (me);
1166 Grob *lvs = last_visible_stem (me);
1168 Grob *commonx = fvs->common_refpoint (lvs, X_AXIS);
1171 Real dx = last_visible_stem (me)->relative_coordinate (commonx, X_AXIS)
1172 - first_visible_stem (me)->relative_coordinate (commonx, X_AXIS);
1173 Real dydx = dy && dx ? dy/dx : 0;
1174 dydx = 0.6 * tanh (dydx) / damping;
1176 Real damped_dy = dydx * dx;
1177 pos[LEFT] += (dy - damped_dy) / 2;
1178 pos[RIGHT] -= (dy - damped_dy) / 2;
1180 me->set_grob_property ("positions", ly_interval2scm (pos));
1182 return SCM_UNSPECIFIED;
1186 Report slice containing the numbers that are both in (car BEAMING)
1190 where_are_the_whole_beams(SCM beaming)
1194 for( SCM s = gh_car (beaming); gh_pair_p (s) ; s = gh_cdr (s))
1196 if (scm_memq (gh_car (s), gh_cdr (beaming)) != SCM_BOOL_F)
1198 l.add_point (gh_scm2int (gh_car (s)));
1204 /* Return the Y position of the stem-end, given the Y-left, Y-right
1205 in POS for stem S. This Y position is relative to S. */
1207 Beam::calc_stem_y (Grob *me, Grob* s, Grob ** common,
1209 Interval pos, bool french)
1211 Real beam_translation = get_beam_translation (me);
1214 Real r = s->relative_coordinate (common[X_AXIS], X_AXIS) - xl;
1215 Real dy = pos.delta ();
1217 Real stem_y_beam0 = (dy && dx
1222 Direction my_dir = get_grob_direction (s);
1223 SCM beaming = s->get_grob_property ("beaming");
1225 Real stem_y = stem_y_beam0;
1228 Slice bm = where_are_the_whole_beams (beaming);
1229 if (!bm.is_empty ())
1230 stem_y += beam_translation * bm[-my_dir];
1234 Slice bm = Stem::beam_multiplicity(s);
1235 if (!bm.is_empty ())
1236 stem_y +=bm[my_dir] * beam_translation;
1239 Real id = me->relative_coordinate (common[Y_AXIS], Y_AXIS)
1240 - s->relative_coordinate (common[Y_AXIS], Y_AXIS);
1246 Hmm. At this time, beam position and slope are determined. Maybe,
1247 stem directions and length should set to relative to the chord's
1248 position of the beam. */
1250 Beam::set_stem_lengths (Grob *me)
1252 Link_array<Grob> stems=
1253 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
1259 for (int a = 2; a--;)
1260 common[a] = common_refpoint_of_array (stems, me, Axis(a));
1262 Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
1263 Real staff_space = Staff_symbol_referencer::staff_space (me);
1267 if (gh_number_p (me->get_grob_property ("gap-count"))
1268 &&gh_scm2int (me->get_grob_property ("gap-count")))
1271 thick = get_thickness(me);
1274 // ugh -> use commonx
1275 Grob * fvs = first_visible_stem (me);
1276 Grob *lvs = last_visible_stem (me);
1278 Real xl = fvs ? fvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
1279 Real xr = lvs ? lvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
1281 for (int i=0; i < stems.size (); i++)
1284 if (Stem::invisible_b (s))
1287 bool french = to_boolean (s->get_grob_property ("french-beaming"));
1288 Real stem_y = calc_stem_y (me, s, common,
1290 pos, french && s != lvs && s!= fvs);
1293 Make the stems go up to the end of the beam. This doesn't matter
1294 for normal beams, but for tremolo beams it looks silly otherwise.
1297 stem_y += thick * 0.5 * get_grob_direction (s);
1299 Stem::set_stemend (s, 2* stem_y / staff_space);
1304 Beam::set_beaming (Grob *me, Beaming_info_list *beaming)
1306 Link_array<Grob> stems=
1307 Pointer_group_interface__extract_grobs (me, (Grob *)0, "stems");
1310 for (int i=0; i < stems.size (); i++)
1313 Don't overwrite user settings.
1318 /* Don't set beaming for outside of outer stems */
1319 if ((d == LEFT && i == 0)
1320 ||(d == RIGHT && i == stems.size () -1))
1323 Grob *st = stems[i];
1324 SCM beaming_prop = st->get_grob_property ("beaming");
1325 if (beaming_prop == SCM_EOL ||
1326 index_get_cell (beaming_prop, d) == SCM_EOL)
1328 int b = beaming->infos_.elem (i).beams_i_drul_[d];
1330 && i < stems.size() -1
1331 && Stem::invisible_b (st))
1332 b = b <? beaming->infos_.elem(i).beams_i_drul_[-d];
1334 Stem::set_beaming (st, b, d);
1337 while (flip (&d) != LEFT);
1342 Beam::forced_stem_count (Grob *me)
1344 Link_array<Grob>stems =
1345 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1347 for (int i=0; i < stems.size (); i++)
1351 if (Stem::invisible_b (s))
1354 /* I can imagine counting those boundaries as a half forced stem,
1355 but let's count them full for now. */
1356 if (abs (Stem::chord_start_y (s)) > 0.1
1357 && (Stem::get_direction (s) != Stem::get_default_dir (s)))
1367 Beam::visible_stem_count (Grob *me)
1369 Link_array<Grob>stems =
1370 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1372 for (int i = stems.size (); i--;)
1374 if (!Stem::invisible_b (stems[i]))
1381 Beam::first_visible_stem (Grob *me)
1383 Link_array<Grob>stems =
1384 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1386 for (int i = 0; i < stems.size (); i++)
1388 if (!Stem::invisible_b (stems[i]))
1395 Beam::last_visible_stem (Grob *me)
1397 Link_array<Grob>stems =
1398 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1399 for (int i = stems.size (); i--;)
1401 if (!Stem::invisible_b (stems[i]))
1411 handle rest under beam (do_post: beams are calculated now)
1412 what about combination of collisions and rest under beam.
1416 rest -> stem -> beam -> interpolate_y_position ()
1418 MAKE_SCHEME_CALLBACK (Beam, rest_collision_callback, 2);
1420 Beam::rest_collision_callback (SCM element_smob, SCM axis)
1422 Grob *rest = unsmob_grob (element_smob);
1423 Axis a = (Axis) gh_scm2int (axis);
1425 assert (a == Y_AXIS);
1427 Grob *st = unsmob_grob (rest->get_grob_property ("stem"));
1430 return gh_double2scm (0.0);
1431 Grob *beam = unsmob_grob (stem->get_grob_property ("beam"));
1433 || !Beam::has_interface (beam)
1434 || !Beam::visible_stem_count (beam))
1435 return gh_double2scm (0.0);
1437 Interval pos (0, 0);
1438 SCM s = beam->get_grob_property ("positions");
1439 if (gh_pair_p (s) && gh_number_p (ly_car (s)))
1440 pos = ly_scm2interval (s);
1442 Real dy = pos.delta ();
1443 // ugh -> use commonx
1444 Real x0 = first_visible_stem (beam)->relative_coordinate (0, X_AXIS);
1445 Real dx = last_visible_stem (beam)->relative_coordinate (0, X_AXIS) - x0;
1446 Real dydx = dy && dx ? dy/dx : 0;
1448 Direction d = Stem::get_direction (stem);
1449 Real stem_y = (pos[LEFT]
1450 + (stem->relative_coordinate (0, X_AXIS) - x0) * dydx)
1453 Real beam_translation = get_beam_translation (beam);
1454 Real beam_thickness = gh_scm2double (beam->get_grob_property ("thickness"));
1455 int beam_count = get_direction_beam_count (beam, d);
1456 Real height_of_my_beams = beam_thickness
1457 + (beam_count - 1) * beam_translation;
1458 Real beam_y = stem_y - height_of_my_beams + beam_thickness / 2.0;
1460 Real staff_space = Staff_symbol_referencer::staff_space (rest);
1462 /* Better calculate relative-distance directly, rather than using
1464 Grob *common_x = rest->common_refpoint (beam, Y_AXIS);
1465 Real rest_dim = rest->extent (common_x, Y_AXIS)[d] / staff_space * d;
1467 Real minimum_distance = gh_scm2double
1468 (rest->get_grob_property ("minimum-beam-collision-distance"));
1470 Real distance = beam_y - rest_dim;
1473 shift = minimum_distance - distance;
1474 else if (minimum_distance > distance)
1475 shift = minimum_distance - distance;
1477 int stafflines = Staff_symbol_referencer::line_count (rest);
1479 /* Always move discretely by half spaces */
1480 Real discrete_shift = ceil (shift * 2.0) / 2.0;
1482 /* Inside staff, move by whole spaces*/
1483 if ((rest->extent (common_x, Y_AXIS)[d] + discrete_shift) * d
1485 ||(rest->extent (common_x, Y_AXIS)[-d] + discrete_shift) * -d
1487 discrete_shift = ceil (discrete_shift);
1489 return gh_double2scm (-d * discrete_shift);
1493 Beam::knee_b (Grob* me)
1495 SCM k = me->get_grob_property ("knee");
1496 if (gh_boolean_p (k))
1497 return gh_scm2bool (k);
1501 for (SCM s = me->get_grob_property ("stems"); gh_pair_p (s); s = ly_cdr (s))
1503 Direction dir = get_grob_direction (unsmob_grob (ly_car (s)));
1512 me->set_grob_property ("knee", gh_bool2scm (knee));
1518 Beam::get_direction_beam_count (Grob *me, Direction d )
1520 Link_array<Grob>stems =
1521 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1524 for (int i = stems.size (); i--;)
1527 Should we take invisible stems into account?
1529 if (Stem::get_direction (stems[i]) == d)
1530 bc = bc >? (Stem::beam_multiplicity (stems[i]).length () + 1);
1537 ADD_INTERFACE (Beam, "beam-interface",
1540 "#'thickness= weight of beams, in staffspace "
1543 "We take the least squares line through the ideal-length stems, and "
1544 "then damp that using "
1546 " damped = tanh (slope) \n"
1548 "this gives an unquantized left and right position for the beam end. "
1549 "Then we take all combinations of quantings near these left and right "
1550 "positions, and give them a score (according to how close they are to "
1551 "the ideal slope, how close the result is to the ideal stems, etc.). We "
1552 "take the best scoring combination. "
1554 "knee 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");