(process_acknowledged_grobs):
[lilypond.git] / lily / beam.cc
blob3b04092c5138d18b02e61add951c1b14ae9fc011
1 /*
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>
8 */
11 TODO:
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.
18 Notes:
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"
32 #include "beaming.hh"
33 #include "beam.hh"
34 #include "misc.hh"
35 #include "least-squares.hh"
36 #include "stem.hh"
37 #include "paper-def.hh"
38 #include "lookup.hh"
39 #include "group-interface.hh"
40 #include "staff-symbol-referencer.hh"
41 #include "item.hh"
42 #include "spanner.hh"
43 #include "warn.hh"
46 #define DEBUG_QUANTING 0
49 #if DEBUG_QUANTING
50 #include "text-item.hh" // debug output.
51 #include "font-interface.hh" // debug output.
52 #endif
55 void
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));
69 Real
70 Beam::get_thickness (Grob * me)
72 SCM th = me->get_grob_property ("thickness");
73 if (gh_number_p (th))
74 return gh_scm2double (th)* Staff_symbol_referencer::staff_space (me);
75 else
76 return 0.0;
79 /* Return the translation between 2 adjoining beams. */
80 Real
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. */
89 int
90 Beam::get_beam_count (Grob *me)
92 int m = 0;
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);
98 return m;
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
125 direction.
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
136 when you do
138 [r8 c8 r8].
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);
144 if (count < 2)
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);
154 me->suicide ();
156 return SCM_UNSPECIFIED;
158 else if (scm_ilength (stems) == 0)
160 me->suicide ();
161 return SCM_UNSPECIFIED;
164 if (count >= 1)
166 Direction d = get_default_dir (me);
168 consider_auto_knees (me);
169 set_stem_directions (me, d);
171 connect_beams (me);
173 set_stem_shorten (me);
176 return SCM_EOL;
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
187 |===|
195 position_with_maximal_common_beams (SCM left_beaming, SCM right_beaming,
196 Direction left_dir,
197 Direction right_dir)
199 Slice lslice = int_list_to_slice (gh_cdr (left_beaming));
201 int best_count = 0;
202 int best_start = 0;
203 for (int i = lslice[-left_dir];
204 (i - lslice[left_dir])* left_dir <= 0 ; i+= left_dir)
206 int count =0;
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)
211 count ++;
214 if (count >= best_count)
216 best_count = count;
217 best_start = i;
221 return best_start;
224 void
225 Beam::connect_beams (Grob *me)
227 Link_array<Grob> stems=
228 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
230 Slice last_int;
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,
244 last_dir, this_dir);
246 Direction d = LEFT;
247 Slice new_slice ;
250 if (d == RIGHT && i == stems.size()-1)
251 continue;
253 new_slice.set_empty();
254 SCM s = index_get_cell (this_beaming, d);
255 for (; gh_pair_p (s); s = gh_cdr (s))
257 int new_beam_pos =
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;
271 else
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;
291 last_dir = this_dir;
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);
305 Real x0, dx;
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;
312 else
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");
319 Interval pos;
320 if (!is_number_pair (posns))
322 programming_error ("No beam posns");
323 pos = Interval (0,0);
325 else
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 ;
338 Real gap_length =0.0;
339 SCM scm_gap = me->get_grob_property ("gap");
340 if (gh_number_p (scm_gap))
341 gap_length = gh_scm2double (scm_gap);
343 Molecule the_beam;
344 Real lt = me->get_paper ()->get_realvar (ly_symbol2scm ("linethickness"));
346 for (int i = 0; i<= stems.size(); i++)
348 Grob * st = (i < stems.size()) ? stems[i] : 0;
350 SCM this_beaming = st ? st->get_grob_property ("beaming") : SCM_EOL;
351 Real xposn = st ? st->relative_coordinate (xcommon, X_AXIS) : 0.0;
352 Real stem_width = st ? gh_scm2double (st->get_grob_property ("thickness")) *lt : 0 ;
353 Direction stem_dir = st ? to_dir (st->get_grob_property ("direction")) : CENTER;
355 We do the space left of ST, with lfliebertjes pointing to the
356 right from the left stem, and rfliebertjes pointing left from
357 right stem.
359 SCM left = (i>0) ? gh_cdr (last_beaming) : SCM_EOL;
360 SCM right = st ? gh_car (this_beaming) : SCM_EOL;
362 Array<int> full_beams;
363 Array<int> lfliebertjes;
364 Array<int> rfliebertjes;
366 for (SCM s = left;
367 gh_pair_p (s); s =gh_cdr (s))
369 int b = gh_scm2int (gh_car (s));
370 if (scm_memq (gh_car(s), right) != SCM_BOOL_F)
372 full_beams.push (b);
374 else
376 lfliebertjes.push (b);
379 for (SCM s = right;
380 gh_pair_p (s); s =gh_cdr (s))
382 int b = gh_scm2int (gh_car (s));
383 if (scm_memq (gh_car(s), left) == SCM_BOOL_F)
385 rfliebertjes.push (b);
390 how much to stick out for beams across linebreaks
392 Real break_overshoot = 3.0;
393 Real w = (i>0 && st)? xposn - last_xposn : break_overshoot;
394 Real stem_offset = 0.0;
395 Real width_corr = 0.0;
396 if (i == 1)
398 stem_offset -= last_width/2;
399 width_corr += last_width/2;
402 if (i == stems.size() -1)
404 width_corr += stem_width/2;
408 Molecule whole = Lookup::beam (dydx, w + width_corr, thick);
409 Molecule gapped;
411 int gap_count = 0;
412 if (gh_number_p (me->get_grob_property ("gap-count")))
414 gap_count = gh_scm2int (me->get_grob_property ("gap-count"));
415 gapped = Lookup::beam (dydx, w + width_corr - 2 * gap_length, thick);
417 full_beams.sort (default_compare);
418 if (stem_dir == UP)
419 full_beams.reverse ();
422 int k = 0;
423 for (int j = full_beams.size (); j--;)
425 Molecule b (whole);
427 if (k++ < gap_count)
429 b = gapped;
430 b.translate_axis (gap_length, X_AXIS);
432 b.translate_axis (last_xposn - x0 + stem_offset, X_AXIS);
433 b.translate_axis (dydx * (last_xposn - x0) + bdy * full_beams[j], Y_AXIS);
435 the_beam.add_molecule (b);
440 if (lfliebertjes.size() || rfliebertjes.size())
442 Real nw_f;
444 if (st)
446 int t = Stem::duration_log (st);
448 SCM proc = me->get_grob_property ("flag-width-function");
449 SCM result = gh_call1 (proc, scm_int2num (t));
450 nw_f = gh_scm2double (result);
452 else
453 nw_f = break_overshoot;
455 /* Half beam should be one note-width,
456 but let's make sure two half-beams never touch */
457 Real w = (i>0 && st) ? (xposn - last_xposn) : break_overshoot;
458 w = w/2 <? nw_f;
460 Molecule half = Lookup::beam (dydx, w, thick);
461 for (int j = lfliebertjes.size(); j--;)
463 Molecule b (half);
464 b.translate_axis (last_xposn - x0, X_AXIS);
465 b.translate_axis (dydx * (last_xposn-x0) + bdy * lfliebertjes[j], Y_AXIS);
466 the_beam.add_molecule (b);
468 for (int j = rfliebertjes.size(); j--;)
470 Molecule b (half);
471 b.translate_axis (xposn - x0 - w , X_AXIS);
472 b.translate_axis (dydx * (xposn-x0 -w) + bdy * rfliebertjes[j], Y_AXIS);
473 the_beam.add_molecule (b);
478 last_xposn = xposn;
479 last_width = stem_width;
480 last_beaming = this_beaming;
483 the_beam.translate_axis (x0 - me->relative_coordinate (xcommon, X_AXIS), X_AXIS);
484 the_beam.translate_axis (pos[LEFT], Y_AXIS);
486 #if (DEBUG_QUANTING)
489 This code prints the demerits for each beam. Perhaps this
490 should be switchable for those who want to twiddle with the
491 parameters.
493 String str;
494 if (1)
496 str += to_string (gh_scm2int (me->get_grob_property ("best-idx")));
497 str += ":";
499 str += to_string (gh_scm2double (me->get_grob_property ("quant-score")),
500 "%.2f");
502 SCM properties = Font_interface::font_alist_chain (me);
504 Molecule tm = Text_item::interpret_new_markup
505 (me->self_scm(), properties, scm_makfrom0str (str.to_str0 ()));
506 the_beam.add_at_edge (Y_AXIS, UP, tm, 5.0, 0);
508 #endif
512 return the_beam.smobbed_copy();
518 Direction
519 Beam::get_default_dir (Grob *me)
521 Drul_array<int> total;
522 total[UP] = total[DOWN] = 0;
523 Drul_array<int> count;
524 count[UP] = count[DOWN] = 0;
525 Direction d = DOWN;
527 Link_array<Grob> stems=
528 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
530 for (int i=0; i <stems.size (); i++)
531 do {
532 Grob *s = stems[i];
533 Direction sd = Directional_element_interface::get (s);
535 int center_distance = int(- d * Stem::head_positions (s) [-d]) >? 0;
536 int current = sd ? (1 + d * sd)/2 : center_distance;
538 if (current)
540 total[d] += current;
541 count[d] ++;
543 } while (flip (&d) != DOWN);
545 SCM func = me->get_grob_property ("dir-function");
546 SCM s = gh_call2 (func,
547 gh_cons (scm_int2num (count[UP]),
548 scm_int2num (count[DOWN])),
549 gh_cons (scm_int2num (total[UP]),
550 scm_int2num (total[DOWN])));
552 if (gh_number_p (s) && gh_scm2int (s))
553 return to_dir (s);
555 /* If dir is not determined: get default */
556 return to_dir (me->get_grob_property ("neutral-direction"));
560 /* Set all stems with non-forced direction to beam direction.
561 Urg: non-forced should become `without/with unforced' direction,
562 once stem gets cleaned-up. */
563 void
564 Beam::set_stem_directions (Grob *me, Direction d)
566 Link_array<Grob> stems
567 =Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
569 for (int i=0; i <stems.size (); i++)
571 Grob *s = stems[i];
573 SCM forcedir = s->get_grob_property ("direction");
574 if (!to_dir (forcedir))
575 Directional_element_interface::set (s, d);
580 A union of intervals in the real line.
582 Abysmal performance (quadratic) for large N, hopefully we don't have
583 that large N. In any case, this should probably be rewritten to use
584 a balanced tree.
586 struct Int_set
588 Array<Interval> allowed_regions_;
590 Int_set()
592 set_full();
595 void set_full()
597 allowed_regions_.clear();
598 Interval s;
599 s.set_full ();
600 allowed_regions_.push (s);
603 void remove_interval (Interval rm)
605 for (int i = 0; i < allowed_regions_.size(); )
607 Interval s = rm;
609 s.intersect (allowed_regions_[i]);
611 if (!s.empty_b ())
613 Interval before = allowed_regions_[i];
614 Interval after = allowed_regions_[i];
616 before[RIGHT] = s[LEFT];
617 after[LEFT] = s[RIGHT];
619 if (!before.empty_b() && before.length () > 0.0)
621 allowed_regions_.insert (before, i);
622 i++;
624 allowed_regions_.del (i);
625 if (!after.empty_b () && after.length () > 0.0)
627 allowed_regions_.insert (after, i);
628 i++;
631 else
632 i++;
639 Only try horizontal beams for knees. No reliable detection of
640 anything else is possible here, since we don't know funky-beaming
641 settings, or X-distances (slopes!) People that want sloped
642 knee-beams, should set the directions manually.
644 void
645 Beam::consider_auto_knees (Grob* me)
647 SCM scm = me->get_grob_property ("auto-knee-gap");
648 if (!gh_number_p (scm))
649 return ;
651 Real threshold = gh_scm2double (scm);
653 Int_set gaps;
655 gaps.set_full ();
657 Link_array<Grob> stems=
658 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
660 Grob *common = common_refpoint_of_array (stems, me, Y_AXIS);
661 Real staff_space = Staff_symbol_referencer::staff_space (me);
663 Array<Interval> hps_array;
664 for (int i=0; i < stems.size (); i++)
666 Grob* stem = stems[i];
667 if (Stem::invisible_b (stem))
668 continue;
670 Interval hps = Stem::head_positions (stem);
671 if(!hps.empty_b())
673 hps[LEFT] += -1;
674 hps[RIGHT] += 1;
675 hps *= staff_space * 0.5 ;
678 We could subtract beam Y position, but this routine only
679 sets stem directions, a constant shift does not have an
680 influence.
683 hps += stem->relative_coordinate (common, Y_AXIS);
685 if (to_dir (stem->get_grob_property ("direction")))
687 Direction stemdir = to_dir (stem->get_grob_property ("direction"));
688 hps[-stemdir] = - stemdir * infinity_f;
691 hps_array.push (hps);
693 gaps.remove_interval (hps);
696 Interval max_gap;
697 Real max_gap_len =0.0;
699 for (int i = gaps.allowed_regions_.size() -1; i >= 0 ; i--)
701 Interval gap = gaps.allowed_regions_[i];
704 the outer gaps are not knees.
706 if (isinf (gap[LEFT]) || isinf(gap[RIGHT]))
707 continue;
709 if (gap.length () >= max_gap_len)
711 max_gap_len = gap.length();
712 max_gap = gap;
716 if (max_gap_len > threshold)
718 int j = 0;
719 for (int i = 0; i < stems.size(); i++)
721 Grob* stem = stems[i];
722 if (Stem::invisible_b (stem))
723 continue;
725 Interval hps = hps_array[j++];
728 Direction d = (hps.center () < max_gap.center()) ?
729 UP : DOWN ;
731 stem->set_grob_property ("direction", scm_int2num (d));
733 hps.intersect (max_gap);
734 assert (hps.empty_b () || hps.length () < 1e-6 );
741 /* Set stem's shorten property if unset.
743 TODO:
744 take some y-position (chord/beam/nearest?) into account
745 scmify forced-fraction
747 This is done in beam because the shorten has to be uniform over the
748 entire beam.
751 void
752 Beam::set_stem_shorten (Grob *me)
755 shortening looks silly for x staff beams
757 if (knee_b(me))
758 return ;
760 Real forced_fraction = 1.0 * forced_stem_count (me)
761 / visible_stem_count (me);
763 int beam_count = get_beam_count (me);
765 SCM shorten_list = me->get_grob_property ("beamed-stem-shorten");
766 if (shorten_list == SCM_EOL)
767 return;
769 Real staff_space = Staff_symbol_referencer::staff_space (me);
771 SCM shorten_elt =
772 robust_list_ref (beam_count -1, shorten_list);
773 Real shorten_f = gh_scm2double (shorten_elt) * staff_space;
775 /* your similar cute comment here */
776 shorten_f *= forced_fraction;
778 if (shorten_f)
779 me->set_grob_property ("shorten", gh_double2scm (shorten_f));
782 /* Call list of y-dy-callbacks, that handle setting of
783 grob-properties
786 MAKE_SCHEME_CALLBACK (Beam, after_line_breaking, 1);
788 Beam::after_line_breaking (SCM smob)
790 Grob *me = unsmob_grob (smob);
792 /* Copy to mutable list. */
793 SCM s = ly_deep_copy (me->get_grob_property ("positions"));
794 me->set_grob_property ("positions", s);
796 if (ly_car (s) == SCM_BOOL_F)
799 // one wonders if such genericity is necessary --hwn.
800 SCM callbacks = me->get_grob_property ("position-callbacks");
801 for (SCM i = callbacks; gh_pair_p (i); i = ly_cdr (i))
802 gh_call1 (ly_car (i), smob);
805 set_stem_lengths (me);
806 return SCM_UNSPECIFIED;
811 Compute a first approximation to the beam slope.
813 MAKE_SCHEME_CALLBACK (Beam, least_squares, 1);
815 Beam::least_squares (SCM smob)
817 Grob *me = unsmob_grob (smob);
819 int count = visible_stem_count (me);
820 Interval pos (0, 0);
822 if (count < 1)
824 me->set_grob_property ("positions", ly_interval2scm (pos));
825 return SCM_UNSPECIFIED;
829 Array<Real> x_posns ;
830 Link_array<Grob> stems=
831 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
832 Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
833 Grob *commony = common_refpoint_of_array (stems, me, Y_AXIS);
835 Real my_y = me->relative_coordinate (commony, Y_AXIS);
837 Grob *fvs = first_visible_stem (me);
838 Grob *lvs = last_visible_stem (me);
840 Interval ideal (Stem::get_stem_info (fvs).ideal_y_
841 + fvs->relative_coordinate (commony, Y_AXIS) -my_y,
842 Stem::get_stem_info (lvs).ideal_y_
843 + lvs->relative_coordinate (commony, Y_AXIS) - my_y);
845 Real x0 = first_visible_stem (me)->relative_coordinate (commonx, X_AXIS);
846 for (int i=0; i < stems.size (); i++)
848 Grob* s = stems[i];
850 Real x = s->relative_coordinate (commonx, X_AXIS) - x0;
851 x_posns.push (x);
853 Real dx = last_visible_stem (me)->relative_coordinate (commonx, X_AXIS) - x0;
856 Real y =0;
857 Real dydx = 0;
858 Real dy = 0;
860 if (!ideal.delta ())
862 Interval chord (Stem::chord_start_y (first_visible_stem (me)),
863 Stem::chord_start_y (last_visible_stem (me)));
865 /* Simple beams (2 stems) on middle line should be allowed to be
866 slightly sloped.
868 However, if both stems reach middle line,
869 ideal[LEFT] == ideal[RIGHT] and ideal.delta () == 0.
871 For that case, we apply artificial slope */
872 if (!ideal[LEFT] && chord.delta () && count == 2)
874 /* FIXME. -> UP */
875 Direction d = (Direction) (sign (chord.delta ()) * UP);
876 pos[d] = gh_scm2double (me->get_grob_property ("thickness")) / 2;
877 pos[-d] = - pos[d];
879 else
881 pos = ideal;
885 For broken beams this doesn't work well. In this case, the
886 slope esp. of the first part of a broken beam should predict
887 where the second part goes.
890 y = pos[LEFT];
891 dy = pos[RIGHT]- y;
892 dydx = dy/dx;
897 else
899 Array<Offset> ideals;
900 for (int i=0; i < stems.size (); i++)
902 Grob* s = stems[i];
903 if (Stem::invisible_b (s))
904 continue;
905 ideals.push (Offset (x_posns[i],
906 Stem::get_stem_info (s).ideal_y_
907 + s->relative_coordinate (commony, Y_AXIS)
908 - my_y));
911 minimise_least_squares (&dydx, &y, ideals);
913 dy = dydx * dx;
914 me->set_grob_property ("least-squares-dy", gh_double2scm (dy));
915 pos = Interval (y, (y+dy));
918 me->set_grob_property ("positions", ly_interval2scm (pos));
920 return SCM_UNSPECIFIED;
925 We can't combine with previous function, since check concave and
926 slope damping comes first.
928 TODO: we should use the concaveness to control the amount of damping
929 applied.
932 MAKE_SCHEME_CALLBACK (Beam, shift_region_to_valid, 1);
934 Beam::shift_region_to_valid (SCM grob)
936 Grob *me = unsmob_grob (grob);
938 Code dup.
940 Array<Real> x_posns ;
941 Link_array<Grob> stems=
942 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
943 Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
944 Grob *commony = common_refpoint_of_array (stems, me, Y_AXIS);
946 Grob *fvs = first_visible_stem (me);
948 if (!fvs)
949 return SCM_UNSPECIFIED;
951 Real x0 =fvs->relative_coordinate (commonx, X_AXIS);
952 for (int i=0; i < stems.size (); i++)
954 Grob* s = stems[i];
956 Real x = s->relative_coordinate (commonx, X_AXIS) - x0;
957 x_posns.push (x);
960 Grob *lvs = last_visible_stem (me);
961 if (!lvs)
962 return SCM_UNSPECIFIED;
964 Real dx = lvs->relative_coordinate (commonx, X_AXIS) - x0;
966 Interval pos = ly_scm2interval ( me->get_grob_property ("positions"));
967 Real dy = pos.delta();
968 Real y = pos[LEFT];
969 Real dydx =dy/dx;
973 Shift the positions so that we have a chance of finding good
974 quants (i.e. no short stem failures.)
976 Interval feasible_left_point;
977 feasible_left_point.set_full ();
978 for (int i=0; i < stems.size (); i++)
980 Grob* s = stems[i];
981 if (Stem::invisible_b (s))
982 continue;
984 Direction d = Stem::get_direction (s);
986 Real left_y =
987 Stem::get_stem_info (s).shortest_y_
988 - dydx * x_posns [i];
991 left_y is now relative to the stem S. We want relative to
992 ourselves, so translate:
994 left_y +=
995 + s->relative_coordinate (commony, Y_AXIS)
996 - me->relative_coordinate (commony, Y_AXIS);
998 Interval flp ;
999 flp.set_full ();
1000 flp[-d] = left_y;
1002 feasible_left_point.intersect (flp);
1005 if (feasible_left_point.empty_b())
1007 warning (_("Not sure that we can find a nice beam slope (no viable initial configuration found)."));
1009 else if (!feasible_left_point.elem_b(y))
1011 if (isinf (feasible_left_point[DOWN]))
1012 y = feasible_left_point[UP] - REGION_SIZE;
1013 else if (isinf (feasible_left_point[UP]))
1014 y = feasible_left_point[DOWN]+ REGION_SIZE;
1015 else
1016 y = feasible_left_point.center ();
1018 pos = Interval (y, (y+dy));
1019 me->set_grob_property ("positions", ly_interval2scm (pos));
1020 return SCM_UNSPECIFIED;
1024 MAKE_SCHEME_CALLBACK (Beam, check_concave, 1);
1026 Beam::check_concave (SCM smob)
1028 Grob *me = unsmob_grob (smob);
1030 Link_array<Grob> stems =
1031 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1033 for (int i = 0; i < stems.size ();)
1035 if (Stem::invisible_b (stems[i]))
1036 stems.del (i);
1037 else
1038 i++;
1041 if (stems.size () < 3)
1042 return SCM_UNSPECIFIED;
1045 /* Concaveness #1: If distance of an inner notehead to line between
1046 two outer noteheads is bigger than CONCAVENESS-GAP (2.0ss),
1047 beam is concave (Heinz Stolba).
1049 In the case of knees, the line connecting outer heads is often
1050 not related to the beam slope (it may even go in the other
1051 direction). Skip the check when the outer stems point in
1052 different directions. --hwn
1055 bool concaveness1 = false;
1056 SCM gap = me->get_grob_property ("concaveness-gap");
1057 if (gh_number_p (gap)
1058 && Stem::get_direction(stems.top ())
1059 == Stem::get_direction(stems[0]))
1061 Real r1 = gh_scm2double (gap);
1062 Real dy = Stem::chord_start_y (stems.top ())
1063 - Stem::chord_start_y (stems[0]);
1066 Real slope = dy / (stems.size () - 1);
1068 Real y0 = Stem::chord_start_y (stems[0]);
1069 for (int i = 1; i < stems.size () - 1; i++)
1071 Real c = (Stem::chord_start_y (stems[i]) - y0) - i * slope;
1072 if (c > r1)
1074 concaveness1 = true;
1075 break;
1081 /* Concaveness #2: Sum distances of inner noteheads that fall
1082 outside the interval of the two outer noteheads.
1084 We only do this for beams where first and last stem have the same
1085 direction. --hwn.
1088 Note that "convex" stems compensate for "concave" stems.
1089 (is that intentional?) --hwn.
1092 Real concaveness2 = 0;
1093 SCM thresh = me->get_grob_property ("concaveness-threshold");
1094 Real r2 = infinity_f;
1095 if (!concaveness1 && gh_number_p (thresh)
1096 && Stem::get_direction(stems.top ())
1097 == Stem::get_direction(stems[0]))
1099 r2 = gh_scm2double (thresh);
1101 Direction dir = Stem::get_direction(stems.top ());
1102 Real concave = 0;
1103 Interval iv (Stem::chord_start_y (stems[0]),
1104 Stem::chord_start_y (stems.top ()));
1106 if (iv[MAX] < iv[MIN])
1107 iv.swap ();
1109 for (int i = 1; i < stems.size () - 1; i++)
1111 Real f = Stem::chord_start_y (stems[i]);
1112 concave += ((f - iv[MAX] ) >? 0) +
1113 ((f - iv[MIN] ) <? 0);
1115 concave *= dir;
1116 concaveness2 = concave / (stems.size () - 2);
1120 ugh: this is the a kludge to get
1121 input/regression/beam-concave.ly to behave as
1122 baerenreiter.
1127 huh? we're dividing twice (which is not scalable) meaning that
1128 the longer the beam, the more unlikely it will be
1129 concave. Maybe you would even expect the other way around??
1131 --hwn.
1134 concaveness2 /= (stems.size () - 2);
1137 /* TODO: some sort of damping iso -> plain horizontal */
1138 if (concaveness1 || concaveness2 > r2)
1140 Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
1141 Real r = pos.linear_combination (0);
1142 me->set_grob_property ("positions", ly_interval2scm (Interval (r, r)));
1143 me->set_grob_property ("least-squares-dy", gh_double2scm (0));
1146 return SCM_UNSPECIFIED;
1149 /* This neat trick is by Werner Lemberg,
1150 damped = tanh (slope)
1151 corresponds with some tables in [Wanske] CHECKME */
1152 MAKE_SCHEME_CALLBACK (Beam, slope_damping, 1);
1154 Beam::slope_damping (SCM smob)
1156 Grob *me = unsmob_grob (smob);
1158 if (visible_stem_count (me) <= 1)
1159 return SCM_UNSPECIFIED;
1161 SCM s = me->get_grob_property ("damping");
1162 int damping = gh_scm2int (s);
1164 if (damping)
1166 Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
1167 Real dy = pos.delta ();
1169 Grob *fvs = first_visible_stem (me);
1170 Grob *lvs = last_visible_stem (me);
1172 Grob *commonx = fvs->common_refpoint (lvs, X_AXIS);
1175 Real dx = last_visible_stem (me)->relative_coordinate (commonx, X_AXIS)
1176 - first_visible_stem (me)->relative_coordinate (commonx, X_AXIS);
1177 Real dydx = dy && dx ? dy/dx : 0;
1178 dydx = 0.6 * tanh (dydx) / damping;
1180 Real damped_dy = dydx * dx;
1181 pos[LEFT] += (dy - damped_dy) / 2;
1182 pos[RIGHT] -= (dy - damped_dy) / 2;
1184 me->set_grob_property ("positions", ly_interval2scm (pos));
1186 return SCM_UNSPECIFIED;
1190 Report slice containing the numbers that are both in (car BEAMING)
1191 and (cdr BEAMING)
1193 Slice
1194 where_are_the_whole_beams(SCM beaming)
1196 Slice l;
1198 for( SCM s = gh_car (beaming); gh_pair_p (s) ; s = gh_cdr (s))
1200 if (scm_memq (gh_car (s), gh_cdr (beaming)) != SCM_BOOL_F)
1202 l.add_point (gh_scm2int (gh_car (s)));
1205 return l;
1208 /* Return the Y position of the stem-end, given the Y-left, Y-right
1209 in POS for stem S. This Y position is relative to S. */
1210 Real
1211 Beam::calc_stem_y (Grob *me, Grob* s, Grob ** common,
1212 Real xl, Real xr,
1213 Interval pos, bool french)
1215 Real beam_translation = get_beam_translation (me);
1218 Real r = s->relative_coordinate (common[X_AXIS], X_AXIS) - xl;
1219 Real dy = pos.delta ();
1220 Real dx = xr - xl;
1221 Real stem_y_beam0 = (dy && dx
1222 ? r / dx
1223 * dy
1224 : 0) + pos[LEFT];
1226 Direction my_dir = Directional_element_interface::get (s);
1227 SCM beaming = s->get_grob_property ("beaming");
1229 Real stem_y = stem_y_beam0;
1230 if (french)
1232 Slice bm = where_are_the_whole_beams (beaming);
1233 if (!bm.empty_b())
1234 stem_y += beam_translation * bm[-my_dir];
1236 else
1238 Slice bm = Stem::beam_multiplicity(s);
1239 if (!bm.empty_b())
1240 stem_y +=bm[my_dir] * beam_translation;
1243 Real id = me->relative_coordinate (common[Y_AXIS], Y_AXIS)
1244 - s->relative_coordinate (common[Y_AXIS], Y_AXIS);
1246 return stem_y + id;
1250 Hmm. At this time, beam position and slope are determined. Maybe,
1251 stem directions and length should set to relative to the chord's
1252 position of the beam. */
1253 void
1254 Beam::set_stem_lengths (Grob *me)
1256 Link_array<Grob> stems=
1257 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
1259 if (!stems.size ())
1260 return;
1262 Grob *common[2];
1263 for (int a = 2; a--;)
1264 common[a] = common_refpoint_of_array (stems, me, Axis(a));
1266 Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
1267 Real staff_space = Staff_symbol_referencer::staff_space (me);
1269 bool gap = false;
1270 Real thick =0.0;
1271 if (gh_number_p (me->get_grob_property ("gap-count"))
1272 &&gh_scm2int (me->get_grob_property ("gap-count")))
1274 gap = true;
1275 thick = get_thickness(me);
1278 // ugh -> use commonx
1279 Grob * fvs = first_visible_stem (me);
1280 Grob *lvs = last_visible_stem (me);
1282 Real xl = fvs ? fvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
1283 Real xr = lvs ? lvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
1285 for (int i=0; i < stems.size (); i++)
1287 Grob* s = stems[i];
1288 if (Stem::invisible_b (s))
1289 continue;
1291 bool french = to_boolean (s->get_grob_property ("french-beaming"));
1292 Real stem_y = calc_stem_y (me, s, common,
1293 xl, xr,
1294 pos, french && s != lvs && s!= fvs);
1297 Make the stems go up to the end of the beam. This doesn't matter
1298 for normal beams, but for tremolo beams it looks silly otherwise.
1300 if (gap)
1301 stem_y += thick * 0.5 * Directional_element_interface::get(s);
1303 Stem::set_stemend (s, 2* stem_y / staff_space);
1307 void
1308 Beam::set_beaming (Grob *me, Beaming_info_list *beaming)
1310 Link_array<Grob> stems=
1311 Pointer_group_interface__extract_grobs (me, (Grob *)0, "stems");
1313 Direction d = LEFT;
1314 for (int i=0; i < stems.size (); i++)
1317 Don't overwrite user settings.
1322 /* Don't set beaming for outside of outer stems */
1323 if ((d == LEFT && i == 0)
1324 ||(d == RIGHT && i == stems.size () -1))
1325 continue;
1327 Grob *st = stems[i];
1328 SCM beaming_prop = st->get_grob_property ("beaming");
1329 if (beaming_prop == SCM_EOL ||
1330 index_get_cell (beaming_prop, d) == SCM_EOL)
1332 int b = beaming->infos_.elem (i).beams_i_drul_[d];
1333 if (i>0
1334 && i < stems.size() -1
1335 && Stem::invisible_b (st))
1336 b = b <? beaming->infos_.elem(i).beams_i_drul_[-d];
1338 Stem::set_beaming (st, b, d);
1341 while (flip (&d) != LEFT);
1346 Beam::forced_stem_count (Grob *me)
1348 Link_array<Grob>stems =
1349 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1350 int f = 0;
1351 for (int i=0; i < stems.size (); i++)
1353 Grob *s = stems[i];
1355 if (Stem::invisible_b (s))
1356 continue;
1358 /* I can imagine counting those boundaries as a half forced stem,
1359 but let's count them full for now. */
1360 if (abs (Stem::chord_start_y (s)) > 0.1
1361 && (Stem::get_direction (s) != Stem::get_default_dir (s)))
1362 f++;
1364 return f;
1371 Beam::visible_stem_count (Grob *me)
1373 Link_array<Grob>stems =
1374 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1375 int c = 0;
1376 for (int i = stems.size (); i--;)
1378 if (!Stem::invisible_b (stems[i]))
1379 c++;
1381 return c;
1384 Grob*
1385 Beam::first_visible_stem (Grob *me)
1387 Link_array<Grob>stems =
1388 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1390 for (int i = 0; i < stems.size (); i++)
1392 if (!Stem::invisible_b (stems[i]))
1393 return stems[i];
1395 return 0;
1398 Grob*
1399 Beam::last_visible_stem (Grob *me)
1401 Link_array<Grob>stems =
1402 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1403 for (int i = stems.size (); i--;)
1405 if (!Stem::invisible_b (stems[i]))
1406 return stems[i];
1408 return 0;
1413 [TODO]
1415 handle rest under beam (do_post: beams are calculated now)
1416 what about combination of collisions and rest under beam.
1418 Should lookup
1420 rest -> stem -> beam -> interpolate_y_position ()
1422 MAKE_SCHEME_CALLBACK (Beam, rest_collision_callback, 2);
1424 Beam::rest_collision_callback (SCM element_smob, SCM axis)
1426 Grob *rest = unsmob_grob (element_smob);
1427 Axis a = (Axis) gh_scm2int (axis);
1429 assert (a == Y_AXIS);
1431 Grob *st = unsmob_grob (rest->get_grob_property ("stem"));
1432 Grob *stem = st;
1433 if (!stem)
1434 return gh_double2scm (0.0);
1435 Grob *beam = unsmob_grob (stem->get_grob_property ("beam"));
1436 if (!beam
1437 || !Beam::has_interface (beam)
1438 || !Beam::visible_stem_count (beam))
1439 return gh_double2scm (0.0);
1441 Interval pos (0, 0);
1442 SCM s = beam->get_grob_property ("positions");
1443 if (gh_pair_p (s) && gh_number_p (ly_car (s)))
1444 pos = ly_scm2interval (s);
1446 Real dy = pos.delta ();
1447 // ugh -> use commonx
1448 Real x0 = first_visible_stem (beam)->relative_coordinate (0, X_AXIS);
1449 Real dx = last_visible_stem (beam)->relative_coordinate (0, X_AXIS) - x0;
1450 Real dydx = dy && dx ? dy/dx : 0;
1452 Direction d = Stem::get_direction (stem);
1453 Real stem_y = (pos[LEFT]
1454 + (stem->relative_coordinate (0, X_AXIS) - x0) * dydx)
1455 * d;
1457 Real beam_translation = get_beam_translation (beam);
1458 Real beam_thickness = gh_scm2double (beam->get_grob_property ("thickness"));
1459 int beam_count = get_direction_beam_count (beam, d);
1460 Real height_of_my_beams = beam_thickness
1461 + (beam_count - 1) * beam_translation;
1462 Real beam_y = stem_y - height_of_my_beams + beam_thickness / 2.0;
1464 Real staff_space = Staff_symbol_referencer::staff_space (rest);
1466 /* Better calculate relative-distance directly, rather than using
1467 rest_dim? */
1468 Grob *common_x = rest->common_refpoint (beam, Y_AXIS);
1469 Real rest_dim = rest->extent (common_x, Y_AXIS)[d] / staff_space * d;
1471 Real minimum_distance = gh_scm2double
1472 (rest->get_grob_property ("minimum-beam-collision-distance"));
1474 Real distance = beam_y - rest_dim;
1475 Real shift = 0;
1476 if (distance < 0)
1477 shift = minimum_distance - distance;
1478 else if (minimum_distance > distance)
1479 shift = minimum_distance - distance;
1481 int stafflines = Staff_symbol_referencer::line_count (rest);
1483 /* Always move discretely by half spaces */
1484 Real discrete_shift = ceil (shift * 2.0) / 2.0;
1486 /* Inside staff, move by whole spaces*/
1487 if ((rest->extent (common_x, Y_AXIS)[d] + discrete_shift) * d
1488 < stafflines / 2.0
1489 ||(rest->extent (common_x, Y_AXIS)[-d] + discrete_shift) * -d
1490 < stafflines / 2.0)
1491 discrete_shift = ceil (discrete_shift);
1493 return gh_double2scm (-d * discrete_shift);
1496 bool
1497 Beam::knee_b (Grob* me)
1499 SCM k = me->get_grob_property ("knee");
1500 if (gh_boolean_p (k))
1501 return gh_scm2bool (k);
1503 bool knee = false;
1504 int d = 0;
1505 for (SCM s = me->get_grob_property ("stems"); gh_pair_p (s); s = ly_cdr (s))
1507 Direction dir = Directional_element_interface::get
1508 (unsmob_grob (ly_car (s)));
1509 if (d && d != dir)
1511 knee = true;
1512 break;
1514 d = dir;
1517 me->set_grob_property ("knee", gh_bool2scm (knee));
1519 return knee;
1523 Beam::get_direction_beam_count (Grob *me, Direction d )
1525 Link_array<Grob>stems =
1526 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1527 int bc = 0;
1529 for (int i = stems.size (); i--;)
1532 Should we take invisible stems into account?
1534 if (Stem::get_direction (stems[i]) == d)
1535 bc = bc >? (Stem::beam_multiplicity (stems[i]).length () + 1);
1538 return bc;
1542 ADD_INTERFACE (Beam, "beam-interface",
1543 "A beam. \n\n"
1545 "#'thickness= weight of beams, in staffspace "
1548 "We take the least squares line through the ideal-length stems, and "
1549 "then damp that using "
1550 " \n"
1551 " damped = tanh (slope) \n"
1552 " \n"
1553 "this gives an unquantized left and right position for the beam end. "
1554 "Then we take all combinations of quantings near these left and right "
1555 "positions, and give them a score (according to how close they are to "
1556 "the ideal slope, how close the result is to the ideal stems, etc.). We "
1557 "take the best scoring combination. "
1559 "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");