*** empty log message ***
[lilypond.git] / lily / beam.cc
blobef9f56ec379305a1af08ab9412734447e3f4f037
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 (!ly_number_pair_p (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 ;
339 SCM gap = me->get_grob_property ("gap");
340 Molecule the_beam;
341 Real lt = me->get_paper ()->get_realvar (ly_symbol2scm ("linethickness"));
343 for (int i = 0; i<= stems.size(); i++)
345 Grob * st = (i < stems.size()) ? stems[i] : 0;
347 SCM this_beaming = st ? st->get_grob_property ("beaming") : SCM_EOL;
348 Real xposn = st ? st->relative_coordinate (xcommon, X_AXIS) : 0.0;
349 Real stem_width = st ? gh_scm2double (st->get_grob_property ("thickness")) *lt : 0 ;
352 We do the space left of ST, with lfliebertjes pointing to the
353 right from the left stem, and rfliebertjes pointing left from
354 right stem.
356 SCM left = (i>0) ? gh_cdr (last_beaming) : SCM_EOL;
357 SCM right = st ? gh_car (this_beaming) : SCM_EOL;
359 Array<int> fullbeams;
360 Array<int> lfliebertjes;
361 Array<int> rfliebertjes;
363 for (SCM s = left;
364 gh_pair_p (s); s =gh_cdr (s))
366 int b = gh_scm2int (gh_car (s));
367 if (scm_memq (gh_car(s), right) != SCM_BOOL_F)
369 fullbeams.push (b);
371 else
373 lfliebertjes.push (b);
376 for (SCM s = right;
377 gh_pair_p (s); s =gh_cdr (s))
379 int b = gh_scm2int (gh_car (s));
380 if (scm_memq (gh_car(s), left) == SCM_BOOL_F)
382 rfliebertjes.push (b);
387 how much to stick out for beams across linebreaks
389 Real break_overshoot = 3.0;
390 Real w = (i>0 && st)? xposn - last_xposn : break_overshoot;
391 Real stem_offset = 0.0;
392 Real width_corr = 0.0;
393 if (i == 1)
395 stem_offset -= last_width/2;
396 width_corr += last_width/2;
399 if (i == stems.size() -1)
401 width_corr += stem_width/2;
404 if (gh_number_p (gap))
406 Real g = gh_scm2double (gap);
407 stem_offset += g;
408 width_corr -= 2*g;
411 Molecule whole = Lookup::beam (dydx, w + width_corr, thick);
412 for (int j = fullbeams.size(); j--;)
414 Molecule b (whole);
415 b.translate_axis (last_xposn - x0 + stem_offset, X_AXIS);
416 b.translate_axis (dydx * (last_xposn - x0) + bdy * fullbeams[j], Y_AXIS);
417 the_beam.add_molecule (b);
420 if (lfliebertjes.size() || rfliebertjes.size())
422 Real nw_f;
424 if (st)
426 int t = Stem::duration_log (st);
428 SCM proc = me->get_grob_property ("flag-width-function");
429 SCM result = gh_call1 (proc, scm_int2num (t));
430 nw_f = gh_scm2double (result);
432 else
433 nw_f = break_overshoot;
435 /* Half beam should be one note-width,
436 but let's make sure two half-beams never touch */
437 Real w = (i>0 && st) ? (xposn - last_xposn) : break_overshoot;
438 w = w/2 <? nw_f;
440 Molecule half = Lookup::beam (dydx, w, thick);
441 for (int j = lfliebertjes.size(); j--;)
443 Molecule b (half);
444 b.translate_axis (last_xposn - x0, X_AXIS);
445 b.translate_axis (dydx * (last_xposn-x0) + bdy * lfliebertjes[j], Y_AXIS);
446 the_beam.add_molecule (b);
448 for (int j = rfliebertjes.size(); j--;)
450 Molecule b (half);
451 b.translate_axis (xposn - x0 - w , X_AXIS);
452 b.translate_axis (dydx * (xposn-x0 -w) + bdy * rfliebertjes[j], Y_AXIS);
453 the_beam.add_molecule (b);
458 last_xposn = xposn;
459 last_width = stem_width;
460 last_beaming = this_beaming;
463 the_beam.translate_axis (x0 - me->relative_coordinate (xcommon, X_AXIS), X_AXIS);
464 the_beam.translate_axis (pos[LEFT], Y_AXIS);
466 #if (DEBUG_QUANTING)
469 This code prints the demerits for each beam. Perhaps this
470 should be switchable for those who want to twiddle with the
471 parameters.
473 String str;
474 if (1)
476 str += to_string (gh_scm2int (me->get_grob_property ("best-idx")));
477 str += ":";
479 str += to_string (gh_scm2double (me->get_grob_property ("quant-score")),
480 "%.2f");
482 SCM properties = Font_interface::font_alist_chain (me);
484 Molecule tm = Text_item::interpret_new_markup
485 (me->self_scm(), properties, scm_makfrom0str (str.to_str0 ()));
486 the_beam.add_at_edge (Y_AXIS, UP, tm, 5.0, 0);
488 #endif
492 return the_beam.smobbed_copy();
498 Direction
499 Beam::get_default_dir (Grob *me)
501 Drul_array<int> total;
502 total[UP] = total[DOWN] = 0;
503 Drul_array<int> count;
504 count[UP] = count[DOWN] = 0;
505 Direction d = DOWN;
507 Link_array<Grob> stems=
508 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
510 for (int i=0; i <stems.size (); i++)
511 do {
512 Grob *s = stems[i];
513 Direction sd = Directional_element_interface::get (s);
515 int center_distance = int(- d * Stem::head_positions (s) [-d]) >? 0;
516 int current = sd ? (1 + d * sd)/2 : center_distance;
518 if (current)
520 total[d] += current;
521 count[d] ++;
523 } while (flip (&d) != DOWN);
525 SCM func = me->get_grob_property ("dir-function");
526 SCM s = gh_call2 (func,
527 gh_cons (scm_int2num (count[UP]),
528 scm_int2num (count[DOWN])),
529 gh_cons (scm_int2num (total[UP]),
530 scm_int2num (total[DOWN])));
532 if (gh_number_p (s) && gh_scm2int (s))
533 return to_dir (s);
535 /* If dir is not determined: get default */
536 return to_dir (me->get_grob_property ("neutral-direction"));
540 /* Set all stems with non-forced direction to beam direction.
541 Urg: non-forced should become `without/with unforced' direction,
542 once stem gets cleaned-up. */
543 void
544 Beam::set_stem_directions (Grob *me, Direction d)
546 Link_array<Grob> stems
547 =Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
549 for (int i=0; i <stems.size (); i++)
551 Grob *s = stems[i];
553 SCM forcedir = s->get_grob_property ("direction");
554 if (!to_dir (forcedir))
555 Directional_element_interface::set (s, d);
560 A union of intervals in the real line.
562 Abysmal performance (quadratic) for large N, hopefully we don't have
563 that large N. In any case, this should probably be rewritten to use
564 a balanced tree.
566 struct Int_set
568 Array<Interval> allowed_regions_;
570 Int_set()
572 set_full();
575 void set_full()
577 allowed_regions_.clear();
578 Interval s;
579 s.set_full ();
580 allowed_regions_.push (s);
583 void remove_interval (Interval rm)
585 for (int i = 0; i < allowed_regions_.size(); )
587 Interval s = rm;
589 s.intersect (allowed_regions_[i]);
591 if (!s.empty_b ())
593 Interval before = allowed_regions_[i];
594 Interval after = allowed_regions_[i];
596 before[RIGHT] = s[LEFT];
597 after[LEFT] = s[RIGHT];
599 if (!before.empty_b() && before.length () > 0.0)
601 allowed_regions_.insert (before, i);
602 i++;
604 allowed_regions_.del (i);
605 if (!after.empty_b () && after.length () > 0.0)
607 allowed_regions_.insert (after, i);
608 i++;
611 else
612 i++;
619 Only try horizontal beams for knees. No reliable detection of
620 anything else is possible here, since we don't know funky-beaming
621 settings, or X-distances (slopes!) People that want sloped
622 knee-beams, should set the directions manually.
624 void
625 Beam::consider_auto_knees (Grob* me)
627 SCM scm = me->get_grob_property ("auto-knee-gap");
628 if (!gh_number_p (scm))
629 return ;
631 Real threshold = gh_scm2double (scm);
633 Int_set gaps;
635 gaps.set_full ();
637 Link_array<Grob> stems=
638 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
640 Grob *common = common_refpoint_of_array (stems, me, Y_AXIS);
641 Real staff_space = Staff_symbol_referencer::staff_space (me);
643 Array<Interval> hps_array;
644 for (int i=0; i < stems.size (); i++)
646 Grob* stem = stems[i];
647 if (Stem::invisible_b (stem))
648 continue;
650 Interval hps = Stem::head_positions (stem);
651 if(!hps.empty_b())
653 hps[LEFT] += -1;
654 hps[RIGHT] += 1;
655 hps *= staff_space * 0.5 ;
658 We could subtract beam Y position, but this routine only
659 sets stem directions, a constant shift does not have an
660 influence.
663 hps += stem->relative_coordinate (common, Y_AXIS);
665 if (to_dir (stem->get_grob_property ("direction")))
667 Direction stemdir = to_dir (stem->get_grob_property ("direction"));
668 hps[-stemdir] = - stemdir * infinity_f;
671 hps_array.push (hps);
673 gaps.remove_interval (hps);
676 Interval max_gap;
677 Real max_gap_len =0.0;
679 for (int i = gaps.allowed_regions_.size() -1; i >= 0 ; i--)
681 Interval gap = gaps.allowed_regions_[i];
684 the outer gaps are not knees.
686 if (isinf (gap[LEFT]) || isinf(gap[RIGHT]))
687 continue;
689 if (gap.length () >= max_gap_len)
691 max_gap_len = gap.length();
692 max_gap = gap;
696 if (max_gap_len > threshold)
698 int j = 0;
699 for (int i = 0; i < stems.size(); i++)
701 Grob* stem = stems[i];
702 if (Stem::invisible_b (stem))
703 continue;
705 Interval hps = hps_array[j++];
708 Direction d = (hps.center () < max_gap.center()) ?
709 UP : DOWN ;
711 stem->set_grob_property ("direction", scm_int2num (d));
713 hps.intersect (max_gap);
714 assert (hps.empty_b () || hps.length () < 1e-6 );
721 /* Set stem's shorten property if unset.
723 TODO:
724 take some y-position (chord/beam/nearest?) into account
725 scmify forced-fraction
727 This is done in beam because the shorten has to be uniform over the
728 entire beam.
731 void
732 Beam::set_stem_shorten (Grob *me)
735 shortening looks silly for x staff beams
737 if (knee_b(me))
738 return ;
740 Real forced_fraction = 1.0 * forced_stem_count (me)
741 / visible_stem_count (me);
743 int beam_count = get_beam_count (me);
745 SCM shorten_list = me->get_grob_property ("beamed-stem-shorten");
746 if (shorten_list == SCM_EOL)
747 return;
749 Real staff_space = Staff_symbol_referencer::staff_space (me);
751 SCM shorten_elt =
752 robust_list_ref (beam_count -1, shorten_list);
753 Real shorten_f = gh_scm2double (shorten_elt) * staff_space;
755 /* your similar cute comment here */
756 shorten_f *= forced_fraction;
758 if (shorten_f)
759 me->set_grob_property ("shorten", gh_double2scm (shorten_f));
762 /* Call list of y-dy-callbacks, that handle setting of
763 grob-properties
766 MAKE_SCHEME_CALLBACK (Beam, after_line_breaking, 1);
768 Beam::after_line_breaking (SCM smob)
770 Grob *me = unsmob_grob (smob);
772 /* Copy to mutable list. */
773 SCM s = ly_deep_copy (me->get_grob_property ("positions"));
774 me->set_grob_property ("positions", s);
776 if (ly_car (s) == SCM_BOOL_F)
779 // one wonders if such genericity is necessary --hwn.
780 SCM callbacks = me->get_grob_property ("position-callbacks");
781 for (SCM i = callbacks; gh_pair_p (i); i = ly_cdr (i))
782 gh_call1 (ly_car (i), smob);
785 set_stem_lengths (me);
786 return SCM_UNSPECIFIED;
791 Compute a first approximation to the beam slope.
793 MAKE_SCHEME_CALLBACK (Beam, least_squares, 1);
795 Beam::least_squares (SCM smob)
797 Grob *me = unsmob_grob (smob);
799 int count = visible_stem_count (me);
800 Interval pos (0, 0);
802 if (count <= 1)
804 me->set_grob_property ("positions", ly_interval2scm (pos));
805 return SCM_UNSPECIFIED;
809 Array<Real> x_posns ;
810 Link_array<Grob> stems=
811 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
812 Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
813 Grob *commony = common_refpoint_of_array (stems, me, Y_AXIS);
815 Real my_y = me->relative_coordinate (commony, Y_AXIS);
817 Grob *fvs = first_visible_stem (me);
818 Grob *lvs = last_visible_stem (me);
820 Interval ideal (Stem::get_stem_info (fvs).ideal_y_
821 + fvs->relative_coordinate (commony, Y_AXIS) -my_y,
822 Stem::get_stem_info (lvs).ideal_y_
823 + lvs->relative_coordinate (commony, Y_AXIS) - my_y);
825 Real x0 = first_visible_stem (me)->relative_coordinate (commonx, X_AXIS);
826 for (int i=0; i < stems.size (); i++)
828 Grob* s = stems[i];
830 Real x = s->relative_coordinate (commonx, X_AXIS) - x0;
831 x_posns.push (x);
833 Real dx = last_visible_stem (me)->relative_coordinate (commonx, X_AXIS) - x0;
835 Real y =0;
836 Real dydx = 0;
837 Real dy = 0;
839 if (!ideal.delta ())
841 Interval chord (Stem::chord_start_y (first_visible_stem (me)),
842 Stem::chord_start_y (last_visible_stem (me)));
844 /* Simple beams (2 stems) on middle line should be allowed to be
845 slightly sloped.
847 However, if both stems reach middle line,
848 ideal[LEFT] == ideal[RIGHT] and ideal.delta () == 0.
850 For that case, we apply artificial slope */
851 if (!ideal[LEFT] && chord.delta () && count == 2)
853 /* FIXME. -> UP */
854 Direction d = (Direction) (sign (chord.delta ()) * UP);
855 pos[d] = gh_scm2double (me->get_grob_property ("thickness")) / 2;
856 pos[-d] = - pos[d];
858 else
860 pos = ideal;
863 y = pos[LEFT];
864 dy = pos[RIGHT]- y;
865 dydx = dy/dx;
867 else
869 Array<Offset> ideals;
870 for (int i=0; i < stems.size (); i++)
872 Grob* s = stems[i];
873 if (Stem::invisible_b (s))
874 continue;
875 ideals.push (Offset (x_posns[i],
876 Stem::get_stem_info (s).ideal_y_
877 + s->relative_coordinate (commony, Y_AXIS)
878 - my_y));
880 minimise_least_squares (&dydx, &y, ideals);
882 dy = dydx * dx;
883 me->set_grob_property ("least-squares-dy", gh_double2scm (dy));
884 pos = Interval (y, (y+dy));
887 me->set_grob_property ("positions", ly_interval2scm (pos));
889 return SCM_UNSPECIFIED;
894 We can't combine with previous function, since check concave and
895 slope damping comes first.
897 TODO: we should use the concaveness to control the amount of damping
898 applied.
901 MAKE_SCHEME_CALLBACK (Beam, shift_region_to_valid, 1);
903 Beam::shift_region_to_valid (SCM grob)
905 Grob *me = unsmob_grob (grob);
907 Code dup.
909 Array<Real> x_posns ;
910 Link_array<Grob> stems=
911 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
912 Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
913 Grob *commony = common_refpoint_of_array (stems, me, Y_AXIS);
915 Grob *fvs = first_visible_stem (me);
917 if (!fvs)
918 return SCM_UNSPECIFIED;
920 Real x0 =fvs->relative_coordinate (commonx, X_AXIS);
921 for (int i=0; i < stems.size (); i++)
923 Grob* s = stems[i];
925 Real x = s->relative_coordinate (commonx, X_AXIS) - x0;
926 x_posns.push (x);
929 Grob *lvs = last_visible_stem (me);
930 if (!lvs)
931 return SCM_UNSPECIFIED;
933 Real dx = lvs->relative_coordinate (commonx, X_AXIS) - x0;
935 Interval pos = ly_scm2interval ( me->get_grob_property ("positions"));
936 Real dy = pos.delta();
937 Real y = pos[LEFT];
938 Real dydx =dy/dx;
942 Shift the positions so that we have a chance of finding good
943 quants (i.e. no short stem failures.)
945 Interval feasible_left_point;
946 feasible_left_point.set_full ();
947 for (int i=0; i < stems.size (); i++)
949 Grob* s = stems[i];
950 if (Stem::invisible_b (s))
951 continue;
953 Direction d = Stem::get_direction (s);
955 Real left_y =
956 Stem::get_stem_info (s).shortest_y_
957 - dydx * x_posns [i];
960 left_y is now relative to the stem S. We want relative to
961 ourselves, so translate:
963 left_y +=
964 + s->relative_coordinate (commony, Y_AXIS)
965 - me->relative_coordinate (commony, Y_AXIS);
967 Interval flp ;
968 flp.set_full ();
969 flp[-d] = left_y;
971 feasible_left_point.intersect (flp);
974 if (feasible_left_point.empty_b())
976 warning (_("Not sure that we can find a nice beam slope (no viable initial configuration found)."));
978 else if (!feasible_left_point.elem_b(y))
980 if (isinf (feasible_left_point[DOWN]))
981 y = feasible_left_point[UP] - REGION_SIZE;
982 else if (isinf (feasible_left_point[UP]))
983 y = feasible_left_point[DOWN]+ REGION_SIZE;
984 else
985 y = feasible_left_point.center ();
987 pos = Interval (y, (y+dy));
988 me->set_grob_property ("positions", ly_interval2scm (pos));
989 return SCM_UNSPECIFIED;
993 MAKE_SCHEME_CALLBACK (Beam, check_concave, 1);
995 Beam::check_concave (SCM smob)
997 Grob *me = unsmob_grob (smob);
999 Link_array<Grob> stems =
1000 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1002 for (int i = 0; i < stems.size ();)
1004 if (Stem::invisible_b (stems[i]))
1005 stems.del (i);
1006 else
1007 i++;
1010 if (stems.size () < 3)
1011 return SCM_UNSPECIFIED;
1014 /* Concaveness #1: If distance of an inner notehead to line between
1015 two outer noteheads is bigger than CONCAVENESS-GAP (2.0ss),
1016 beam is concave (Heinz Stolba).
1018 In the case of knees, the line connecting outer heads is often
1019 not related to the beam slope (it may even go in the other
1020 direction). Skip the check when the outer stems point in
1021 different directions. --hwn
1024 bool concaveness1 = false;
1025 SCM gap = me->get_grob_property ("concaveness-gap");
1026 if (gh_number_p (gap)
1027 && Stem::get_direction(stems.top ())
1028 == Stem::get_direction(stems[0]))
1030 Real r1 = gh_scm2double (gap);
1031 Real dy = Stem::chord_start_y (stems.top ())
1032 - Stem::chord_start_y (stems[0]);
1035 Real slope = dy / (stems.size () - 1);
1037 Real y0 = Stem::chord_start_y (stems[0]);
1038 for (int i = 1; i < stems.size () - 1; i++)
1040 Real c = (Stem::chord_start_y (stems[i]) - y0) - i * slope;
1041 if (c > r1)
1043 concaveness1 = true;
1044 break;
1050 /* Concaveness #2: Sum distances of inner noteheads that fall
1051 outside the interval of the two outer noteheads.
1053 We only do this for beams where first and last stem have the same
1054 direction. --hwn.
1057 Note that "convex" stems compensate for "concave" stems.
1058 (is that intentional?) --hwn.
1061 Real concaveness2 = 0;
1062 SCM thresh = me->get_grob_property ("concaveness-threshold");
1063 Real r2 = infinity_f;
1064 if (!concaveness1 && gh_number_p (thresh)
1065 && Stem::get_direction(stems.top ())
1066 == Stem::get_direction(stems[0]))
1068 r2 = gh_scm2double (thresh);
1070 Direction dir = Stem::get_direction(stems.top ());
1071 Real concave = 0;
1072 Interval iv (Stem::chord_start_y (stems[0]),
1073 Stem::chord_start_y (stems.top ()));
1075 if (iv[MAX] < iv[MIN])
1076 iv.swap ();
1078 for (int i = 1; i < stems.size () - 1; i++)
1080 Real f = Stem::chord_start_y (stems[i]);
1081 concave += ((f - iv[MAX] ) >? 0) +
1082 ((f - iv[MIN] ) <? 0);
1084 concave *= dir;
1085 concaveness2 = concave / (stems.size () - 2);
1089 ugh: this is the a kludge to get
1090 input/regression/beam-concave.ly to behave as
1091 baerenreiter.
1096 huh? we're dividing twice (which is not scalable) meaning that
1097 the longer the beam, the more unlikely it will be
1098 concave. Maybe you would even expect the other way around??
1100 --hwn.
1103 concaveness2 /= (stems.size () - 2);
1106 /* TODO: some sort of damping iso -> plain horizontal */
1107 if (concaveness1 || concaveness2 > r2)
1109 Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
1110 Real r = pos.linear_combination (0);
1111 me->set_grob_property ("positions", ly_interval2scm (Interval (r, r)));
1112 me->set_grob_property ("least-squares-dy", gh_double2scm (0));
1115 return SCM_UNSPECIFIED;
1118 /* This neat trick is by Werner Lemberg,
1119 damped = tanh (slope)
1120 corresponds with some tables in [Wanske] CHECKME */
1121 MAKE_SCHEME_CALLBACK (Beam, slope_damping, 1);
1123 Beam::slope_damping (SCM smob)
1125 Grob *me = unsmob_grob (smob);
1127 if (visible_stem_count (me) <= 1)
1128 return SCM_UNSPECIFIED;
1130 SCM s = me->get_grob_property ("damping");
1131 int damping = gh_scm2int (s);
1133 if (damping)
1135 Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
1136 Real dy = pos.delta ();
1138 Grob *fvs = first_visible_stem (me);
1139 Grob *lvs = last_visible_stem (me);
1141 Grob *commonx = fvs->common_refpoint (lvs, X_AXIS);
1144 Real dx = last_visible_stem (me)->relative_coordinate (commonx, X_AXIS)
1145 - first_visible_stem (me)->relative_coordinate (commonx, X_AXIS);
1146 Real dydx = dy && dx ? dy/dx : 0;
1147 dydx = 0.6 * tanh (dydx) / damping;
1149 Real damped_dy = dydx * dx;
1150 pos[LEFT] += (dy - damped_dy) / 2;
1151 pos[RIGHT] -= (dy - damped_dy) / 2;
1153 me->set_grob_property ("positions", ly_interval2scm (pos));
1155 return SCM_UNSPECIFIED;
1159 Report slice containing the numbers that are both in (car BEAMING)
1160 and (cdr BEAMING)
1162 Slice
1163 where_are_the_whole_beams(SCM beaming)
1165 Slice l;
1167 for( SCM s = gh_car (beaming); gh_pair_p (s) ; s = gh_cdr (s))
1169 if (scm_memq (gh_car (s), gh_cdr (beaming)) != SCM_BOOL_F)
1171 l.add_point (gh_scm2int (gh_car (s)));
1174 return l;
1177 /* Return the Y position of the stem-end, given the Y-left, Y-right
1178 in POS for stem S. This Y position is relative to S. */
1179 Real
1180 Beam::calc_stem_y (Grob *me, Grob* s, Grob ** common,
1181 Real xl, Real xr,
1182 Interval pos, bool french)
1184 Real beam_translation = get_beam_translation (me);
1187 Real r = s->relative_coordinate (common[X_AXIS], X_AXIS) - xl;
1188 Real dy = pos.delta ();
1189 Real dx = xr - xl;
1190 Real stem_y_beam0 = (dy && dx
1191 ? r / dx
1192 * dy
1193 : 0) + pos[LEFT];
1195 Direction my_dir = Directional_element_interface::get (s);
1196 SCM beaming = s->get_grob_property ("beaming");
1198 Real stem_y = stem_y_beam0;
1199 if (french)
1201 Slice bm = where_are_the_whole_beams (beaming);
1202 if (!bm.empty_b())
1203 stem_y += beam_translation * bm[-my_dir];
1205 else
1207 Slice bm = Stem::beam_multiplicity(s);
1208 if (!bm.empty_b())
1209 stem_y +=bm[my_dir] * beam_translation;
1212 Real id = me->relative_coordinate (common[Y_AXIS], Y_AXIS)
1213 - s->relative_coordinate (common[Y_AXIS], Y_AXIS);
1215 return stem_y + id;
1219 Hmm. At this time, beam position and slope are determined. Maybe,
1220 stem directions and length should set to relative to the chord's
1221 position of the beam. */
1222 void
1223 Beam::set_stem_lengths (Grob *me)
1225 Link_array<Grob> stems=
1226 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
1228 if (stems.size () <= 1)
1229 return;
1231 Grob *common[2];
1232 for (int a = 2; a--;)
1233 common[a] = common_refpoint_of_array (stems, me, Axis(a));
1235 Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
1236 Real staff_space = Staff_symbol_referencer::staff_space (me);
1238 bool gap = false;
1239 Real thick =0.0;
1240 if (gh_number_p (me->get_grob_property ("gap"))
1241 &&gh_scm2double (me->get_grob_property ("gap")))
1243 gap = true;
1244 thick = get_thickness(me);
1247 // ugh -> use commonx
1248 Grob * fvs = first_visible_stem (me);
1249 Grob *lvs = last_visible_stem (me);
1251 Real xl = fvs ? fvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
1252 Real xr = lvs ? lvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
1254 for (int i=0; i < stems.size (); i++)
1256 Grob* s = stems[i];
1257 if (Stem::invisible_b (s))
1258 continue;
1261 bool french = to_boolean (s->get_grob_property ("french-beaming"));
1262 Real stem_y = calc_stem_y (me, s, common,
1263 xl, xr,
1264 pos, french && i > 0&& (i < stems.size () -1));
1267 Make the stems go up to the end of the beam. This doesn't matter
1268 for normal beams, but for tremolo beams it looks silly otherwise.
1270 if (gap)
1271 stem_y += thick * 0.5 * Directional_element_interface::get(s);
1273 Stem::set_stemend (s, 2* stem_y / staff_space);
1277 void
1278 Beam::set_beaming (Grob *me, Beaming_info_list *beaming)
1280 Link_array<Grob> stems=
1281 Pointer_group_interface__extract_grobs (me, (Grob *)0, "stems");
1283 Direction d = LEFT;
1284 for (int i=0; i < stems.size (); i++)
1287 Don't overwrite user settings.
1292 /* Don't set beaming for outside of outer stems */
1293 if ((d == LEFT && i == 0)
1294 ||(d == RIGHT && i == stems.size () -1))
1295 continue;
1297 Grob *st = stems[i];
1298 SCM beaming_prop = st->get_grob_property ("beaming");
1299 if (beaming_prop == SCM_EOL ||
1300 index_get_cell (beaming_prop, d) == SCM_EOL)
1302 int b = beaming->infos_.elem (i).beams_i_drul_[d];
1303 if (i>0
1304 && i < stems.size() -1
1305 && Stem::invisible_b (st))
1306 b = b <? beaming->infos_.elem(i).beams_i_drul_[-d];
1308 Stem::set_beaming (st, b, d);
1311 while (flip (&d) != LEFT);
1316 Beam::forced_stem_count (Grob *me)
1318 Link_array<Grob>stems =
1319 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1320 int f = 0;
1321 for (int i=0; i < stems.size (); i++)
1323 Grob *s = stems[i];
1325 if (Stem::invisible_b (s))
1326 continue;
1328 /* I can imagine counting those boundaries as a half forced stem,
1329 but let's count them full for now. */
1330 if (abs (Stem::chord_start_y (s)) > 0.1
1331 && (Stem::get_direction (s) != Stem::get_default_dir (s)))
1332 f++;
1334 return f;
1341 Beam::visible_stem_count (Grob *me)
1343 Link_array<Grob>stems =
1344 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1345 int c = 0;
1346 for (int i = stems.size (); i--;)
1348 if (!Stem::invisible_b (stems[i]))
1349 c++;
1351 return c;
1354 Grob*
1355 Beam::first_visible_stem (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++)
1362 if (!Stem::invisible_b (stems[i]))
1363 return stems[i];
1365 return 0;
1368 Grob*
1369 Beam::last_visible_stem (Grob *me)
1371 Link_array<Grob>stems =
1372 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1373 for (int i = stems.size (); i--;)
1375 if (!Stem::invisible_b (stems[i]))
1376 return stems[i];
1378 return 0;
1383 [TODO]
1385 handle rest under beam (do_post: beams are calculated now)
1386 what about combination of collisions and rest under beam.
1388 Should lookup
1390 rest -> stem -> beam -> interpolate_y_position ()
1392 MAKE_SCHEME_CALLBACK (Beam, rest_collision_callback, 2);
1394 Beam::rest_collision_callback (SCM element_smob, SCM axis)
1396 Grob *rest = unsmob_grob (element_smob);
1397 Axis a = (Axis) gh_scm2int (axis);
1399 assert (a == Y_AXIS);
1401 Grob *st = unsmob_grob (rest->get_grob_property ("stem"));
1402 Grob *stem = st;
1403 if (!stem)
1404 return gh_double2scm (0.0);
1405 Grob *beam = unsmob_grob (stem->get_grob_property ("beam"));
1406 if (!beam
1407 || !Beam::has_interface (beam)
1408 || !Beam::visible_stem_count (beam))
1409 return gh_double2scm (0.0);
1411 Interval pos (0, 0);
1412 SCM s = beam->get_grob_property ("positions");
1413 if (gh_pair_p (s) && gh_number_p (ly_car (s)))
1414 pos = ly_scm2interval (s);
1416 Real dy = pos.delta ();
1417 // ugh -> use commonx
1418 Real x0 = first_visible_stem (beam)->relative_coordinate (0, X_AXIS);
1419 Real dx = last_visible_stem (beam)->relative_coordinate (0, X_AXIS) - x0;
1420 Real dydx = dy && dx ? dy/dx : 0;
1422 Direction d = Stem::get_direction (stem);
1423 Real stem_y = (pos[LEFT]
1424 + (stem->relative_coordinate (0, X_AXIS) - x0) * dydx)
1425 * d;
1427 Real beam_translation = get_beam_translation (beam);
1428 Real beam_thickness = gh_scm2double (beam->get_grob_property ("thickness"));
1429 int beam_count = get_direction_beam_count (beam, d);
1430 Real height_of_my_beams = beam_thickness
1431 + (beam_count - 1) * beam_translation;
1432 Real beam_y = stem_y - height_of_my_beams + beam_thickness / 2.0;
1434 Real staff_space = Staff_symbol_referencer::staff_space (rest);
1436 /* Better calculate relative-distance directly, rather than using
1437 rest_dim? */
1438 Grob *common_x = rest->common_refpoint (beam, Y_AXIS);
1439 Real rest_dim = rest->extent (common_x, Y_AXIS)[d] / staff_space * d;
1441 Real minimum_distance = gh_scm2double
1442 (rest->get_grob_property ("minimum-beam-collision-distance"));
1444 Real distance = beam_y - rest_dim;
1445 Real shift = 0;
1446 if (distance < 0)
1447 shift = minimum_distance - distance;
1448 else if (minimum_distance > distance)
1449 shift = minimum_distance - distance;
1451 int stafflines = Staff_symbol_referencer::line_count (rest);
1453 /* Always move discretely by half spaces */
1454 Real discrete_shift = ceil (shift * 2.0) / 2.0;
1456 /* Inside staff, move by whole spaces*/
1457 if ((rest->extent (common_x, Y_AXIS)[d] + discrete_shift) * d
1458 < stafflines / 2.0
1459 ||(rest->extent (common_x, Y_AXIS)[-d] + discrete_shift) * -d
1460 < stafflines / 2.0)
1461 discrete_shift = ceil (discrete_shift);
1463 return gh_double2scm (-d * discrete_shift);
1466 bool
1467 Beam::knee_b (Grob* me)
1469 SCM k = me->get_grob_property ("knee");
1470 if (gh_boolean_p (k))
1471 return gh_scm2bool (k);
1473 bool knee = false;
1474 int d = 0;
1475 for (SCM s = me->get_grob_property ("stems"); gh_pair_p (s); s = ly_cdr (s))
1477 Direction dir = Directional_element_interface::get
1478 (unsmob_grob (ly_car (s)));
1479 if (d && d != dir)
1481 knee = true;
1482 break;
1484 d = dir;
1487 me->set_grob_property ("knee", gh_bool2scm (knee));
1489 return knee;
1493 Beam::get_direction_beam_count (Grob *me, Direction d )
1495 Link_array<Grob>stems =
1496 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1497 int bc = 0;
1499 for (int i = stems.size (); i--;)
1502 Should we take invisible stems into account?
1504 if (Stem::get_direction (stems[i]) == d)
1505 bc = bc >? (Stem::beam_multiplicity (stems[i]).length () + 1);
1508 return bc;
1512 ADD_INTERFACE (Beam, "beam-interface",
1513 "A beam. \n\n"
1515 "#'thickness= weight of beams, in staffspace "
1518 "We take the least squares line through the ideal-length stems, and "
1519 "then damp that using "
1520 " \n"
1521 " damped = tanh (slope) \n"
1522 " \n"
1523 "this gives an unquantized left and right position for the beam end. "
1524 "Then we take all combinations of quantings near these left and right "
1525 "positions, and give them a score (according to how close they are to "
1526 "the ideal slope, how close the result is to the ideal stems, etc.). We "
1527 "take the best scoring combination. "
1529 "knee position-callbacks concaveness-gap concaveness-threshold dir-function quant-score auto-knee-gap gap chord-tremolo beamed-stem-shorten shorten least-squares-dy damping flag-width-function neutral-direction positions space-function thickness");