2 beam.cc -- implement Beam
4 source file of the GNU LilyPond music typesetter
6 (c) 1997--2005 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7 Jan Nieuwenhuizen <janneke@gnu.org>
13 - Determine auto knees based on positions if it's set by the user.
15 - the code is littered with * and / staff_space calls for
16 #'positions. Consider moving to real-world coordinates?
18 Problematic issue is user tweaks (user tweaks are in staff-coordinates.)
22 - Stems run to the Y-center of the beam.
24 - beam_translation is the offset between Y centers of the beam.
27 #include <math.h> // tanh.
30 #include "interval-set.hh"
31 #include "directional-element-interface.hh"
34 #include "least-squares.hh"
36 #include "output-def.hh"
38 #include "group-interface.hh"
39 #include "staff-symbol-referencer.hh"
45 #include "text-item.hh" // debug output.
46 #include "font-interface.hh" // debug output.
50 Beam::add_stem (Grob *me, Grob *s)
52 Pointer_group_interface::add_grob (me, ly_symbol2scm ("stems"), s);
54 s->add_dependency (me);
56 assert (!Stem::get_beam (s));
57 s->set_property ("beam", me->self_scm ());
59 add_bound_item (dynamic_cast<Spanner *> (me), dynamic_cast<Item *> (s));
63 Beam::get_thickness (Grob *me)
65 return robust_scm2double (me->get_property ("thickness"), 0)
66 * Staff_symbol_referencer::staff_space (me);
69 /* Return the translation between 2 adjoining beams. */
71 Beam::get_beam_translation (Grob *me)
73 SCM func = me->get_property ("space-function");
75 if (ly_c_procedure_p (func))
77 SCM s = scm_call_2 (func, me->self_scm (), scm_int2num (get_beam_count (me)));
78 return scm_to_double (s);
86 /* Maximum beam_count. */
88 Beam::get_beam_count (Grob *me)
91 for (SCM s = me->get_property ("stems"); scm_is_pair (s); s = scm_cdr (s))
93 Grob *stem = unsmob_grob (scm_car (s));
94 m = m >? (Stem::beam_multiplicity (stem).length () + 1);
100 Space return space between beams.
102 MAKE_SCHEME_CALLBACK (Beam, space_function, 2);
104 Beam::space_function (SCM smob, SCM beam_count)
106 Grob *me = unsmob_grob (smob);
108 Real staff_space = Staff_symbol_referencer::staff_space (me);
109 Real line = Staff_symbol_referencer::line_thickness (me);
110 Real thickness = get_thickness (me);
112 Real beam_translation = scm_to_int (beam_count) < 4
113 ? (2*staff_space + line - thickness) / 2.0
114 : (3*staff_space + line - thickness) / 3.0;
116 return scm_make_real (beam_translation);
119 /* After pre-processing all directions should be set.
120 Several post-processing routines (stem, slur, script) need stem/beam
122 Currenly, this means that beam has set all stem's directions.
123 [Alternatively, stems could set its own directions, according to
124 their beam, during 'final-pre-processing'.] */
125 MAKE_SCHEME_CALLBACK (Beam, before_line_breaking, 1);
127 Beam::before_line_breaking (SCM smob)
129 Grob *me = unsmob_grob (smob);
131 /* Beams with less than 2 two stems don't make much sense, but could happen
136 For a beam that only has one stem, we try to do some disappearance magic:
137 we revert the flag, and move on to The Eternal Engraving Fields. */
139 int count = visible_stem_count (me);
142 me->warning (_ ("beam has less than two visible stems"));
144 SCM stems = me->get_property ("stems");
145 if (scm_ilength (stems) == 1)
147 me->warning (_ ("removing beam with less than two stems"));
149 unsmob_grob (scm_car (stems))->set_property ("beam", SCM_EOL);
152 return SCM_UNSPECIFIED;
154 else if (scm_ilength (stems) == 0)
157 return SCM_UNSPECIFIED;
162 Direction d = get_default_dir (me);
164 consider_auto_knees (me);
165 set_stem_directions (me, d);
169 set_stem_shorten (me);
176 We want a maximal number of shared beams, but if there is choice, we
177 take the one that is closest to the end of the stem. This is for situations like
188 position_with_maximal_common_beams (SCM left_beaming, SCM right_beaming,
192 Slice lslice = int_list_to_slice (scm_cdr (left_beaming));
196 for (int i = lslice[-left_dir];
197 (i - lslice[left_dir])* left_dir <= 0; i+= left_dir)
200 for (SCM s = scm_car (right_beaming); scm_is_pair (s); s = scm_cdr (s))
202 int k = -right_dir * scm_to_int (scm_car (s)) + i;
203 if (scm_c_memq (scm_int2num (k), left_beaming) != SCM_BOOL_F)
207 if (count >= best_count)
218 Beam::connect_beams (Grob *me)
220 Link_array<Grob> stems
221 = extract_grob_array (me, ly_symbol2scm ("stems"));
224 last_int.set_empty ();
225 SCM last_beaming = SCM_EOL;
226 Direction last_dir = CENTER;
227 for (int i = 0; i< stems.size (); i++)
229 Grob *this_stem = stems[i];
230 SCM this_beaming = this_stem->get_property ("beaming");
232 Direction this_dir = get_grob_direction (this_stem);
233 if (scm_is_pair (last_beaming) && scm_is_pair (this_beaming))
235 int start_point = position_with_maximal_common_beams
236 (last_beaming, this_beaming,
243 if (d == RIGHT && i == stems.size () - 1)
246 new_slice.set_empty ();
247 SCM s = index_get_cell (this_beaming, d);
248 for (; scm_is_pair (s); s = scm_cdr (s))
251 = start_point - this_dir * scm_to_int (scm_car (s));
253 new_slice.add_point (new_beam_pos);
254 scm_set_car_x (s, scm_int2num (new_beam_pos));
258 while (flip (&d) != LEFT);
260 if (!new_slice.is_empty ())
261 last_int = new_slice;
265 scm_set_car_x (this_beaming, SCM_EOL);
266 SCM s = scm_cdr (this_beaming);
267 for (; scm_is_pair (s); s = scm_cdr (s))
269 int np = -this_dir * scm_to_int (scm_car (s));
270 scm_set_car_x (s, scm_int2num (np));
271 last_int.add_point (np);
275 if (i == stems.size () -1)
277 scm_set_cdr_x (this_beaming, SCM_EOL);
280 if (scm_ilength (scm_cdr (this_beaming)) > 0)
282 last_beaming = this_beaming;
289 TODO: should not make beams per stem, but per Y-level.
291 MAKE_SCHEME_CALLBACK (Beam, print, 1);
293 Beam::print (SCM grob)
295 Spanner *me = unsmob_spanner (grob);
298 Link_array<Grob> stems
299 = extract_grob_array (me, ly_symbol2scm ("stems"));
300 Grob *xcommon = common_refpoint_of_array (stems, me, X_AXIS);
302 xcommon = me->get_bound (LEFT)->common_refpoint (xcommon, X_AXIS);
303 xcommon = me->get_bound (RIGHT)->common_refpoint (xcommon, X_AXIS);
306 if (visible_stem_count (me))
308 // ugh -> use commonx
309 x0 = first_visible_stem (me)->relative_coordinate (xcommon, X_AXIS);
310 dx = last_visible_stem (me)->relative_coordinate (xcommon, X_AXIS) - x0;
314 x0 = stems[0]->relative_coordinate (xcommon, X_AXIS);
315 dx = stems.top ()->relative_coordinate (xcommon, X_AXIS) - x0;
318 SCM posns = me->get_property ("positions");
319 Drul_array<Real> pos;
320 if (!is_number_pair (posns))
322 programming_error ("No beam posns");
323 pos = Interval (0, 0);
326 pos = ly_scm2realdrul (posns);
328 scale_drul (&pos, Staff_symbol_referencer::staff_space (me));
330 Real dy = pos[RIGHT] - pos[LEFT];
331 Real slope = (dy && dx) ? dy / dx : 0;
333 Real thick = get_thickness (me);
334 Real bdy = get_beam_translation (me);
336 SCM last_beaming = SCM_EOL;
337 Real last_xposn = -1;
338 Real last_stem_width = -1;
340 Real gap_length = robust_scm2double (me->get_property ("gap"), 0.0);
343 Real lt = me->get_layout ()->get_dimension (ly_symbol2scm ("linethickness"));
345 for (int i = 0; i<= stems.size (); i++)
347 Grob *st = (i < stems.size ()) ? stems[i] : 0;
349 SCM this_beaming = st ? st->get_property ("beaming") : SCM_EOL;
350 Real xposn = st ? st->relative_coordinate (xcommon, X_AXIS) : 0.0;
351 Real stem_width = st ? robust_scm2double (st->get_property ("thickness"), 1.0) *lt : 0;
352 Direction stem_dir = st ? to_dir (st->get_property ("direction")) : CENTER;
354 We do the space left of ST, with lfliebertjes pointing to the
355 right from the left stem, and rfliebertjes pointing left from
358 SCM left = (i > 0) ? scm_cdr (last_beaming) : SCM_EOL;
359 SCM right = st ? scm_car (this_beaming) : SCM_EOL;
361 Array<int> full_beams;
362 Array<int> lfliebertjes;
363 Array<int> rfliebertjes;
366 scm_is_pair (s); s = scm_cdr (s))
368 int b = scm_to_int (scm_car (s));
369 if (scm_c_memq (scm_car (s), right) != SCM_BOOL_F)
375 lfliebertjes.push (b);
379 scm_is_pair (s); s = scm_cdr (s))
381 int b = scm_to_int (scm_car (s));
382 if (scm_c_memq (scm_car (s), left) == SCM_BOOL_F)
384 rfliebertjes.push (b);
389 how much to stick out for beams across linebreaks
391 Real break_overshoot = 3.0;
392 Real w = (i > 0 && st) ? (xposn - last_xposn) : break_overshoot;
394 Real stem_offset = 0.0;
397 w += last_stem_width / 2;
398 stem_offset = -last_stem_width / 2;
404 Real blot = me->get_layout ()->get_dimension (ly_symbol2scm ("blotdiameter"));
405 Stencil whole = Lookup::beam (slope, w, thick, blot);
409 if (scm_is_number (me->get_property ("gap-count")))
411 gap_count = scm_to_int (me->get_property ("gap-count"));
412 gapped = Lookup::beam (slope, w - 2 * gap_length, thick, blot);
414 full_beams.sort (default_compare);
416 full_beams.reverse ();
420 for (int j = full_beams.size (); j--;)
427 b.translate_axis (gap_length, X_AXIS);
429 b.translate_axis (last_xposn - x0 + stem_offset, X_AXIS);
430 b.translate_axis (slope * (last_xposn - x0) + bdy * full_beams[j], Y_AXIS);
432 the_beam.add_stencil (b);
435 if (lfliebertjes.size () || rfliebertjes.size ())
441 int t = Stem::duration_log (st);
443 SCM proc = me->get_property ("flag-width-function");
444 SCM result = scm_call_1 (proc, scm_int2num (t));
445 nw_f = scm_to_double (result);
448 nw_f = break_overshoot / 2;
450 /* Half beam should be one note-width,
451 but let's make sure two half-beams never touch */
455 rw = nw_f <? ((xposn - last_xposn) / 2);
458 TODO: 0.5 is a guess.
460 rw = xposn - me->get_bound (LEFT)->extent (xcommon, X_AXIS)[RIGHT]
464 lw = nw_f <? ((xposn - last_xposn) / 2);
466 lw = me->get_bound (RIGHT)->relative_coordinate (xcommon, X_AXIS)
469 Stencil rhalf = Lookup::beam (slope, rw, thick, blot);
470 Stencil lhalf = Lookup::beam (slope, lw, thick, blot);
471 for (int j = lfliebertjes.size (); j--;)
474 b.translate_axis (last_xposn - x0, X_AXIS);
475 b.translate_axis (slope * (last_xposn - x0) + bdy * lfliebertjes[j], Y_AXIS);
476 the_beam.add_stencil (b);
478 for (int j = rfliebertjes.size (); j--;)
481 b.translate_axis (xposn - x0 - rw, X_AXIS);
482 b.translate_axis (slope * (xposn - x0 -rw) + bdy * rfliebertjes[j], Y_AXIS);
483 the_beam.add_stencil (b);
488 last_stem_width = stem_width;
489 last_beaming = this_beaming;
492 the_beam.translate_axis (x0 - me->relative_coordinate (xcommon, X_AXIS), X_AXIS);
493 the_beam.translate_axis (pos[LEFT], Y_AXIS);
496 SCM quant_score = me->get_property ("quant-score");
497 if (to_boolean (me->get_layout ()->lookup_variable (ly_symbol2scm ("debug-beam-quanting")))
498 && scm_is_string (quant_score))
502 This code prints the demerits for each beam. Perhaps this
503 should be switchable for those who want to twiddle with the
507 SCM properties = Font_interface::text_font_alist_chain (me);
509 Direction stem_dir = stems.size () ? to_dir (stems[0]->get_property ("direction")) : UP;
511 Stencil tm = *unsmob_stencil (Text_interface::interpret_markup
512 (me->get_layout ()->self_scm (), properties, quant_score));
513 the_beam.add_at_edge (Y_AXIS, stem_dir, tm, 1.0, 0);
517 return the_beam.smobbed_copy ();
522 Beam::get_default_dir (Grob *me)
524 Drul_array<int> total;
525 total[UP] = total[DOWN] = 0;
526 Drul_array<int> count;
527 count[UP] = count[DOWN] = 0;
530 Link_array<Grob> stems
531 = extract_grob_array (me, ly_symbol2scm ("stems"));
533 for (int i = 0; i < stems.size (); i++)
537 Direction sd = get_grob_direction (s);
539 int center_distance = int (- d * Stem::head_positions (s) [-d]) >? 0;
540 int current = sd ? (1 + d * sd) / 2 : center_distance;
548 while (flip (&d) != DOWN);
550 SCM func = me->get_property ("dir-function");
551 SCM s = scm_call_2 (func,
552 scm_cons (scm_int2num (count[UP]),
553 scm_int2num (count[DOWN])),
554 scm_cons (scm_int2num (total[UP]),
555 scm_int2num (total[DOWN])));
557 if (scm_is_number (s) && scm_to_int (s))
560 /* If dir is not determined: get default */
561 return to_dir (me->get_property ("neutral-direction"));
564 /* Set all stems with non-forced direction to beam direction.
565 Urg: non-forced should become `without/with unforced' direction,
566 once stem gets cleaned-up. */
568 Beam::set_stem_directions (Grob *me, Direction d)
570 Link_array<Grob> stems
571 = extract_grob_array (me, ly_symbol2scm ("stems"));
573 for (int i = 0; i <stems.size (); i++)
577 SCM forcedir = s->get_property ("direction");
578 if (!to_dir (forcedir))
579 set_grob_direction (s, d);
584 Only try horizontal beams for knees. No reliable detection of
585 anything else is possible here, since we don't know funky-beaming
586 settings, or X-distances (slopes!) People that want sloped
587 knee-beams, should set the directions manually.
590 Beam::consider_auto_knees (Grob *me)
592 SCM scm = me->get_property ("auto-knee-gap");
593 if (!scm_is_number (scm))
600 Link_array<Grob> stems
601 = extract_grob_array (me, ly_symbol2scm ("stems"));
603 Grob *common = common_refpoint_of_array (stems, me, Y_AXIS);
604 Real staff_space = Staff_symbol_referencer::staff_space (me);
606 Array<Interval> head_positions_array;
607 for (int i = 0; i < stems.size (); i++)
609 Grob *stem = stems[i];
610 if (Stem::is_invisible (stem))
613 Interval head_positions = Stem::head_positions (stem);
614 if (!head_positions.is_empty ())
616 head_positions[LEFT] += -1;
617 head_positions[RIGHT] += 1;
618 head_positions *= staff_space * 0.5;
621 We could subtract beam Y position, but this routine only
622 sets stem directions, a constant shift does not have an
625 head_positions += stem->relative_coordinate (common, Y_AXIS);
627 if (to_dir (stem->get_property ("direction")))
629 Direction stemdir = to_dir (stem->get_property ("direction"));
630 head_positions[-stemdir] = -stemdir * infinity_f;
633 head_positions_array.push (head_positions);
635 gaps.remove_interval (head_positions);
639 Real max_gap_len = 0.0;
641 for (int i = gaps.allowed_regions_.size () -1; i >= 0; i--)
643 Interval gap = gaps.allowed_regions_[i];
646 the outer gaps are not knees.
648 if (isinf (gap[LEFT]) || isinf (gap[RIGHT]))
651 if (gap.length () >= max_gap_len)
653 max_gap_len = gap.length ();
658 Real beam_translation = get_beam_translation (me);
659 Real beam_thickness = Beam::get_thickness (me);
660 int beam_count = Beam::get_beam_count (me);
661 Real height_of_beams = beam_thickness / 2
662 + (beam_count - 1) * beam_translation;
663 Real threshold = scm_to_double (scm) + height_of_beams;
665 if (max_gap_len > threshold)
668 for (int i = 0; i < stems.size (); i++)
670 Grob *stem = stems[i];
671 if (Stem::is_invisible (stem))
674 Interval head_positions = head_positions_array[j++];
676 Direction d = (head_positions.center () < max_gap.center ()) ?
679 stem->set_property ("direction", scm_int2num (d));
681 head_positions.intersect (max_gap);
682 assert (head_positions.is_empty () || head_positions.length () < 1e-6);
687 /* Set stem's shorten property if unset.
690 take some y-position (chord/beam/nearest?) into account
691 scmify forced-fraction
693 This is done in beam because the shorten has to be uniform over the
697 Beam::set_stem_shorten (Grob *me)
700 shortening looks silly for x staff beams
705 Real forced_fraction = 1.0 * forced_stem_count (me)
706 / visible_stem_count (me);
708 int beam_count = get_beam_count (me);
710 SCM shorten_list = me->get_property ("beamed-stem-shorten");
711 if (shorten_list == SCM_EOL)
714 Real staff_space = Staff_symbol_referencer::staff_space (me);
717 = robust_list_ref (beam_count -1, shorten_list);
718 Real shorten_f = scm_to_double (shorten_elt) * staff_space;
720 /* your similar cute comment here */
721 shorten_f *= forced_fraction;
724 me->set_property ("shorten", scm_make_real (shorten_f));
727 /* Call list of y-dy-callbacks, that handle setting of
730 MAKE_SCHEME_CALLBACK (Beam, after_line_breaking, 1);
732 Beam::after_line_breaking (SCM smob)
734 Grob *me = unsmob_grob (smob);
737 return SCM_UNSPECIFIED;
741 Beam::position_beam (Grob *me)
745 if (to_boolean (me->get_property ("positioning-done")))
748 me->set_property ("positioning-done", SCM_BOOL_T);
750 /* Copy to mutable list. */
751 SCM s = ly_deep_copy (me->get_property ("positions"));
752 me->set_property ("positions", s);
754 if (scm_car (s) == SCM_BOOL_F)
756 // one wonders if such genericity is necessary --hwn.
757 SCM callbacks = me->get_property ("position-callbacks");
758 for (SCM i = callbacks; scm_is_pair (i); i = scm_cdr (i))
759 scm_call_1 (scm_car (i), me->self_scm ());
762 set_stem_lengths (me);
766 set_minimum_dy (Grob *me, Real *dy)
771 If dy is smaller than the smallest quant, we
772 get absurd direction-sign penalties.
775 Real ss = Staff_symbol_referencer::staff_space (me);
776 Real thickness = Beam::get_thickness (me) / ss;
777 Real slt = Staff_symbol_referencer::line_thickness (me) / ss;
778 Real sit = (thickness - slt) / 2;
780 Real hang = 1.0 - (thickness - slt) / 2;
782 *dy = sign (*dy) * (fabs (*dy)
784 (sit <? inter <? hang));
789 Compute a first approximation to the beam slope.
791 MAKE_SCHEME_CALLBACK (Beam, least_squares, 1);
793 Beam::least_squares (SCM smob)
795 Grob *me = unsmob_grob (smob);
797 int count = visible_stem_count (me);
802 me->set_property ("positions", ly_interval2scm (pos));
803 return SCM_UNSPECIFIED;
807 Link_array<Grob> stems
808 = extract_grob_array (me, ly_symbol2scm ("stems"));
809 Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
810 Grob *commony = common_refpoint_of_array (stems, me, Y_AXIS);
812 Real my_y = me->relative_coordinate (commony, Y_AXIS);
814 Grob *fvs = first_visible_stem (me);
815 Grob *lvs = last_visible_stem (me);
817 Interval ideal (Stem::get_stem_info (fvs).ideal_y_
818 + fvs->relative_coordinate (commony, Y_AXIS) -my_y,
819 Stem::get_stem_info (lvs).ideal_y_
820 + lvs->relative_coordinate (commony, Y_AXIS) - my_y);
822 Real x0 = first_visible_stem (me)->relative_coordinate (commonx, X_AXIS);
823 for (int i = 0; i < stems.size (); i++)
827 Real x = s->relative_coordinate (commonx, X_AXIS) - x0;
830 Real dx = last_visible_stem (me)->relative_coordinate (commonx, X_AXIS) - x0;
838 Interval chord (Stem::chord_start_y (first_visible_stem (me)),
839 Stem::chord_start_y (last_visible_stem (me)));
841 /* Simple beams (2 stems) on middle line should be allowed to be
844 However, if both stems reach middle line,
845 ideal[LEFT] == ideal[RIGHT] and ideal.delta () == 0.
847 For that case, we apply artificial slope */
848 if (!ideal[LEFT] && chord.delta () && count == 2)
851 Direction d = (Direction) (sign (chord.delta ()) * UP);
852 pos[d] = get_thickness (me) / 2;
861 For broken beams this doesn't work well. In this case, the
862 slope esp. of the first part of a broken beam should predict
863 where the second part goes.
865 me->set_property ("least-squares-dy",
866 scm_make_real (pos[RIGHT] - pos[LEFT]));
870 Array<Offset> ideals;
871 for (int i = 0; i < stems.size (); i++)
874 if (Stem::is_invisible (s))
876 ideals.push (Offset (x_posns[i],
877 Stem::get_stem_info (s).ideal_y_
878 + s->relative_coordinate (commony, Y_AXIS)
882 minimise_least_squares (&slope, &y, ideals);
886 set_minimum_dy (me, &dy);
887 me->set_property ("least-squares-dy", scm_make_real (dy));
888 pos = Interval (y, (y + dy));
892 "position" is relative to the staff.
894 scale_drul (&pos, 1/ Staff_symbol_referencer::staff_space (me));
896 me->set_property ("positions", ly_interval2scm (pos));
898 return SCM_UNSPECIFIED;
902 We can't combine with previous function, since check concave and
903 slope damping comes first.
905 TODO: we should use the concaveness to control the amount of damping
908 MAKE_SCHEME_CALLBACK (Beam, shift_region_to_valid, 1);
910 Beam::shift_region_to_valid (SCM grob)
912 Grob *me = unsmob_grob (grob);
917 Link_array<Grob> stems
918 = extract_grob_array (me, ly_symbol2scm ("stems"));
919 Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
920 Grob *commony = common_refpoint_of_array (stems, me, Y_AXIS);
922 Grob *fvs = first_visible_stem (me);
925 return SCM_UNSPECIFIED;
927 Real x0 = fvs->relative_coordinate (commonx, X_AXIS);
928 for (int i = 0; i < stems.size (); i++)
932 Real x = s->relative_coordinate (commonx, X_AXIS) - x0;
936 Grob *lvs = last_visible_stem (me);
938 return SCM_UNSPECIFIED;
940 Real dx = lvs->relative_coordinate (commonx, X_AXIS) - x0;
942 Drul_array<Real> pos = ly_scm2interval (me->get_property ("positions"));
944 scale_drul (&pos, Staff_symbol_referencer::staff_space (me));
946 Real dy = pos[RIGHT] - pos[LEFT];
948 Real slope = dy / dx;
951 Shift the positions so that we have a chance of finding good
952 quants (i.e. no short stem failures.)
954 Interval feasible_left_point;
955 feasible_left_point.set_full ();
956 for (int i = 0; i < stems.size (); i++)
959 if (Stem::is_invisible (s))
962 Direction d = Stem::get_direction (s);
965 = Stem::get_stem_info (s).shortest_y_
966 - slope * x_posns [i];
969 left_y is now relative to the stem S. We want relative to
970 ourselves, so translate:
973 += + s->relative_coordinate (commony, Y_AXIS)
974 - me->relative_coordinate (commony, Y_AXIS);
980 feasible_left_point.intersect (flp);
983 if (feasible_left_point.is_empty ())
984 warning (_ ("no viable initial configuration found: may not find good beam slope"));
985 else if (!feasible_left_point.contains (y))
987 if (isinf (feasible_left_point[DOWN]))
988 y = feasible_left_point[UP] - REGION_SIZE;
989 else if (isinf (feasible_left_point[UP]))
990 y = feasible_left_point[DOWN]+ REGION_SIZE;
992 y = feasible_left_point.center ();
995 pos = Drul_array<Real> (y, (y + dy));
996 scale_drul (&pos, 1/ Staff_symbol_referencer::staff_space (me));
998 me->set_property ("positions", ly_interval2scm (pos));
999 return SCM_UNSPECIFIED;
1002 /* This neat trick is by Werner Lemberg,
1003 damped = tanh (slope)
1004 corresponds with some tables in [Wanske] CHECKME */
1005 MAKE_SCHEME_CALLBACK (Beam, slope_damping, 1);
1007 Beam::slope_damping (SCM smob)
1009 Grob *me = unsmob_grob (smob);
1011 if (visible_stem_count (me) <= 1)
1012 return SCM_UNSPECIFIED;
1014 SCM s = me->get_property ("damping");
1015 Real damping = scm_to_double (s);
1019 Drul_array<Real> pos = ly_scm2interval (me->get_property ("positions"));
1020 scale_drul (&pos, Staff_symbol_referencer::staff_space (me));
1022 Real dy = pos[RIGHT] - pos[LEFT];
1024 Grob *fvs = first_visible_stem (me);
1025 Grob *lvs = last_visible_stem (me);
1027 Grob *commonx = fvs->common_refpoint (lvs, X_AXIS);
1029 Real dx = last_visible_stem (me)->relative_coordinate (commonx, X_AXIS)
1030 - first_visible_stem (me)->relative_coordinate (commonx, X_AXIS);
1032 Real slope = dy && dx ? dy / dx : 0;
1034 Real concaveness = robust_scm2double (me->get_property ("concaveness"), 0.0);
1036 slope = 0.6 * tanh (slope) / (damping + concaveness);
1038 Real damped_dy = slope * dx;
1040 set_minimum_dy (me, &damped_dy);
1042 pos[LEFT] += (dy - damped_dy) / 2;
1043 pos[RIGHT] -= (dy - damped_dy) / 2;
1045 scale_drul (&pos, 1 / Staff_symbol_referencer::staff_space (me));
1047 me->set_property ("positions", ly_interval2scm (pos));
1049 return SCM_UNSPECIFIED;
1053 Report slice containing the numbers that are both in (car BEAMING)
1057 where_are_the_whole_beams (SCM beaming)
1061 for (SCM s = scm_car (beaming); scm_is_pair (s); s = scm_cdr (s))
1063 if (scm_c_memq (scm_car (s), scm_cdr (beaming)) != SCM_BOOL_F)
1065 l.add_point (scm_to_int (scm_car (s)));
1071 /* Return the Y position of the stem-end, given the Y-left, Y-right
1072 in POS for stem S. This Y position is relative to S. */
1074 Beam::calc_stem_y (Grob *me, Grob *s, Grob ** common,
1076 Drul_array<Real> pos, bool french)
1078 Real beam_translation = get_beam_translation (me);
1080 Real r = s->relative_coordinate (common[X_AXIS], X_AXIS) - xl;
1081 Real dy = pos[RIGHT] - pos[LEFT];
1083 Real stem_y_beam0 = (dy && dx
1088 Direction my_dir = get_grob_direction (s);
1089 SCM beaming = s->get_property ("beaming");
1091 Real stem_y = stem_y_beam0;
1094 Slice bm = where_are_the_whole_beams (beaming);
1095 if (!bm.is_empty ())
1096 stem_y += beam_translation * bm[-my_dir];
1100 Slice bm = Stem::beam_multiplicity (s);
1101 if (!bm.is_empty ())
1102 stem_y += bm[my_dir] * beam_translation;
1105 Real id = me->relative_coordinate (common[Y_AXIS], Y_AXIS)
1106 - s->relative_coordinate (common[Y_AXIS], Y_AXIS);
1112 Hmm. At this time, beam position and slope are determined. Maybe,
1113 stem directions and length should set to relative to the chord's
1114 position of the beam. */
1116 Beam::set_stem_lengths (Grob *me)
1118 Link_array<Grob> stems
1119 = extract_grob_array (me, ly_symbol2scm ("stems"));
1125 for (int a = 2; a--;)
1126 common[a] = common_refpoint_of_array (stems, me, Axis (a));
1128 Drul_array<Real> pos = ly_scm2realdrul (me->get_property ("positions"));
1129 Real staff_space = Staff_symbol_referencer::staff_space (me);
1130 scale_drul (&pos, staff_space);
1134 if (scm_is_number (me->get_property ("gap-count"))
1135 &&scm_to_int (me->get_property ("gap-count")))
1138 thick = get_thickness (me);
1141 // ugh -> use commonx
1142 Grob *fvs = first_visible_stem (me);
1143 Grob *lvs = last_visible_stem (me);
1145 Real xl = fvs ? fvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
1146 Real xr = lvs ? lvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
1148 for (int i = 0; i < stems.size (); i++)
1151 if (Stem::is_invisible (s))
1154 bool french = to_boolean (s->get_property ("french-beaming"));
1155 Real stem_y = calc_stem_y (me, s, common,
1157 pos, french && s != lvs && s!= fvs);
1160 Make the stems go up to the end of the beam. This doesn't matter
1161 for normal beams, but for tremolo beams it looks silly otherwise.
1164 stem_y += thick * 0.5 * get_grob_direction (s);
1166 Stem::set_stemend (s, 2* stem_y / staff_space);
1171 Beam::set_beaming (Grob *me, Beaming_info_list *beaming)
1173 Link_array<Grob> stems
1174 = extract_grob_array (me, ly_symbol2scm ("stems"));
1177 for (int i = 0; i < stems.size (); i++)
1180 Don't overwrite user settings.
1185 /* Don't set beaming for outside of outer stems */
1186 if ((d == LEFT && i == 0)
1187 || (d == RIGHT && i == stems.size () -1))
1190 Grob *st = stems[i];
1191 SCM beaming_prop = st->get_property ("beaming");
1192 if (beaming_prop == SCM_EOL
1193 || index_get_cell (beaming_prop, d) == SCM_EOL)
1195 int b = beaming->infos_.elem (i).beams_i_drul_[d];
1197 && i < stems.size () -1
1198 && Stem::is_invisible (st))
1199 b = b <? beaming->infos_.elem (i).beams_i_drul_[-d];
1201 Stem::set_beaming (st, b, d);
1204 while (flip (&d) != LEFT);
1209 Beam::forced_stem_count (Grob *me)
1211 Link_array<Grob> stems
1212 = extract_grob_array (me, ly_symbol2scm ("stems"));
1214 for (int i = 0; i < stems.size (); i++)
1218 if (Stem::is_invisible (s))
1221 /* I can imagine counting those boundaries as a half forced stem,
1222 but let's count them full for now. */
1223 if (abs (Stem::chord_start_y (s)) > 0.1
1224 && (Stem::get_direction (s) != Stem::get_default_dir (s)))
1232 Beam::visible_stem_count (Grob *me)
1234 Link_array<Grob> stems
1235 = extract_grob_array (me, ly_symbol2scm ("stems"));
1237 for (int i = stems.size (); i--;)
1239 if (!Stem::is_invisible (stems[i]))
1246 Beam::first_visible_stem (Grob *me)
1248 Link_array<Grob> stems
1249 = extract_grob_array (me, ly_symbol2scm ("stems"));
1251 for (int i = 0; i < stems.size (); i++)
1253 if (!Stem::is_invisible (stems[i]))
1260 Beam::last_visible_stem (Grob *me)
1262 Link_array<Grob> stems
1263 = extract_grob_array (me, ly_symbol2scm ("stems"));
1264 for (int i = stems.size (); i--;)
1266 if (!Stem::is_invisible (stems[i]))
1275 handle rest under beam (do_post: beams are calculated now)
1276 what about combination of collisions and rest under beam.
1280 rest -> stem -> beam -> interpolate_y_position ()
1282 MAKE_SCHEME_CALLBACK (Beam, rest_collision_callback, 2);
1284 Beam::rest_collision_callback (SCM element_smob, SCM axis)
1286 Grob *rest = unsmob_grob (element_smob);
1287 Axis a = (Axis) scm_to_int (axis);
1289 if (scm_is_number (rest->get_property ("staff-position")))
1290 return scm_int2num (0);
1292 assert (a == Y_AXIS);
1294 Grob *st = unsmob_grob (rest->get_property ("stem"));
1297 return scm_make_real (0.0);
1298 Grob *beam = unsmob_grob (stem->get_property ("beam"));
1300 || !Beam::has_interface (beam)
1301 || !Beam::visible_stem_count (beam))
1302 return scm_make_real (0.0);
1304 Drul_array<Real> pos (0, 0);
1305 SCM s = beam->get_property ("positions");
1306 if (scm_is_pair (s) && scm_is_number (scm_car (s)))
1307 pos = ly_scm2interval (s);
1308 Real staff_space = Staff_symbol_referencer::staff_space (rest);
1310 scale_drul (&pos, staff_space);
1312 Real dy = pos[RIGHT] - pos[LEFT];
1314 // ugh -> use commonx
1315 Real x0 = first_visible_stem (beam)->relative_coordinate (0, X_AXIS);
1316 Real dx = last_visible_stem (beam)->relative_coordinate (0, X_AXIS) - x0;
1317 Real slope = dy && dx ? dy / dx : 0;
1319 Direction d = Stem::get_direction (stem);
1320 Real stem_y = pos[LEFT] + (stem->relative_coordinate (0, X_AXIS) - x0) * slope;
1322 Real beam_translation = get_beam_translation (beam);
1323 Real beam_thickness = Beam::get_thickness (beam);
1326 TODO: this is not strictly correct for 16th knee beams.
1329 = Stem::beam_multiplicity (stem).length () + 1;
1331 Real height_of_my_beams = beam_thickness / 2
1332 + (beam_count - 1) * beam_translation;
1333 Real beam_y = stem_y - d * height_of_my_beams;
1335 Grob *common_y = rest->common_refpoint (beam, Y_AXIS);
1337 Real rest_dim = rest->extent (common_y, Y_AXIS)[d];
1338 Real minimum_distance
1339 = + staff_space * (robust_scm2double (stem->get_property ("stemlet-length"), 0.0)
1340 + robust_scm2double (rest->get_property ("minimum-distance"), 0.0));
1342 Real shift = d * (((beam_y - d * minimum_distance) - rest_dim) * d <? 0.0);
1344 shift /= staff_space;
1345 Real rad = Staff_symbol_referencer::line_count (rest) * staff_space / 2;
1347 /* Always move discretely by half spaces */
1348 shift = ceil (fabs (shift * 2.0)) / 2.0 * sign (shift);
1350 /* Inside staff, move by whole spaces*/
1351 if ((rest->extent (common_y, Y_AXIS)[d] + staff_space * shift) * d
1353 || (rest->extent (common_y, Y_AXIS)[-d] + staff_space * shift) * -d
1355 shift = ceil (fabs (shift)) *sign (shift);
1357 return scm_make_real (staff_space * shift);
1361 Beam::is_knee (Grob *me)
1363 SCM k = me->get_property ("knee");
1364 if (scm_is_bool (k))
1365 return ly_scm2bool (k);
1369 for (SCM s = me->get_property ("stems"); scm_is_pair (s); s = scm_cdr (s))
1371 Direction dir = get_grob_direction (unsmob_grob (scm_car (s)));
1380 me->set_property ("knee", ly_bool2scm (knee));
1386 Beam::get_direction_beam_count (Grob *me, Direction d)
1388 Link_array<Grob> stems
1389 = extract_grob_array (me, ly_symbol2scm ("stems"));
1392 for (int i = stems.size (); i--;)
1395 Should we take invisible stems into account?
1397 if (Stem::get_direction (stems[i]) == d)
1398 bc = bc >? (Stem::beam_multiplicity (stems[i]).length () + 1);
1404 ADD_INTERFACE (Beam, "beam-interface",
1406 "The @code{thickness} property is the weight of beams, and is measured "
1408 "knee positioning-done position-callbacks "
1409 "concaveness dir-function quant-score auto-knee-gap gap "
1410 "gap-count chord-tremolo beamed-stem-shorten shorten least-squares-dy "
1411 "damping inspect-quants flag-width-function neutral-direction positions space-function "