2 beam.cc -- implement Beam
4 source file of the GNU LilyPond music typesetter
6 (c) 1997--2001 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7 Jan Nieuwenhuizen <janneke@gnu.org>
14 -* shorter! (now +- 1000 lines)
18 -* Remove #'direction from beam. The beam has no direction per se.
19 It may only set directions for stems.
23 #include <math.h> // tanh.
25 #include "molecule.hh"
26 #include "directional-element-interface.hh"
30 #include "least-squares.hh"
32 #include "paper-def.hh"
34 #include "group-interface.hh"
35 #include "staff-symbol-referencer.hh"
41 Beam::add_stem (Grob*me, Grob*s)
43 Pointer_group_interface:: add_element (me, "stems", s);
45 s->add_dependency (me);
47 assert (!Stem::beam_l (s));
48 s->set_grob_property ("beam", me->self_scm ());
50 add_bound_item (dynamic_cast<Spanner*> (me), dynamic_cast<Item*> (s));
54 Beam::get_multiplicity (Grob*me)
57 for (SCM s = me->get_grob_property ("stems"); gh_pair_p (s); s = ly_cdr (s))
59 Grob * sc = unsmob_grob (ly_car (s));
61 if (Stem::has_interface (sc))
62 m = m >? Stem::beam_count (sc,LEFT) >? Stem::beam_count (sc,RIGHT);
68 After pre-processing all directions should be set.
69 Several post-processing routines (stem, slur, script) need stem/beam
71 Currenly, this means that beam has set all stem's directions.
72 [Alternatively, stems could set its own directions, according to
73 their beam, during 'final-pre-processing'.]
75 MAKE_SCHEME_CALLBACK (Beam,before_line_breaking,1);
77 Beam::before_line_breaking (SCM smob)
79 Grob * me = unsmob_grob (smob);
83 Why what? Why the warning (beams with less than 2 stems are
84 degenerate beams, should never happen), or why would this ever
85 happen (don't know). */
86 if (visible_stem_count (me) < 2)
88 warning (_ ("beam has less than two stems"));
90 if (visible_stem_count (me) >= 1)
92 if (!Directional_element_interface::get (me))
93 Directional_element_interface::set (me, get_default_dir (me));
95 consider_auto_knees (me);
96 set_stem_directions (me);
97 set_stem_shorten (me);
103 Beam::get_default_dir (Grob*me)
105 Drul_array<int> total;
106 total[UP] = total[DOWN] = 0;
107 Drul_array<int> count;
108 count[UP] = count[DOWN] = 0;
111 Link_array<Item> stems=
112 Pointer_group_interface__extract_elements (me, (Item*)0, "stems");
114 for (int i=0; i <stems.size (); i++)
117 Direction sd = Directional_element_interface::get (s);
118 int current = sd ? (1 + d * sd)/2
119 : Stem::get_center_distance (s, (Direction)-d);
127 } while (flip (&d) != DOWN);
129 SCM func = me->get_grob_property ("dir-function");
130 SCM s = gh_call2 (func,
131 gh_cons (gh_int2scm (count[UP]),
132 gh_int2scm (count[DOWN])),
133 gh_cons (gh_int2scm (total[UP]),
134 gh_int2scm (total[DOWN])));
136 if (gh_number_p (s) && gh_scm2int (s))
140 If dir is not determined: get default
142 return to_dir (me->get_grob_property ("neutral-direction"));
147 Set all stems with non-forced direction to beam direction.
148 Urg: non-forced should become `without/with unforced' direction,
149 once stem gets cleaned-up.
152 Beam::set_stem_directions (Grob*me)
154 Link_array<Item> stems
155 =Pointer_group_interface__extract_elements (me, (Item*) 0, "stems");
156 Direction d = Directional_element_interface::get (me);
158 for (int i=0; i <stems.size (); i++)
161 SCM force = s->remove_grob_property ("dir-forced");
162 if (!gh_boolean_p (force) || !gh_scm2bool (force))
163 Directional_element_interface ::set (s,d);
168 Simplistic auto-knees; only consider vertical gap between two
171 `Forced' stem directions are ignored. If you don't want auto-knees,
172 don't set, or unset auto-knee-gap.
175 Beam::consider_auto_knees (Grob *me)
177 SCM scm = me->get_grob_property ("auto-knee-gap");
179 if (gh_number_p (scm))
183 Real staff_space = Staff_symbol_referencer::staff_space (me);
184 Real gap = gh_scm2double (scm) / staff_space;
186 Direction d = Directional_element_interface::get (me);
187 Link_array<Item> stems=
188 Pointer_group_interface__extract_elements (me, (Item*)0, "stems");
190 Grob *common = me->common_refpoint (stems[0], Y_AXIS);
191 for (int i=1; i < stems.size (); i++)
192 if (!Stem::invisible_b (stems[i]))
193 common = common->common_refpoint (stems[i], Y_AXIS);
196 for (int i=1; i < stems.size (); i++)
198 if (!Stem::invisible_b (stems[i-1]))
200 if (Stem::invisible_b (stems[l]))
202 if (Stem::invisible_b (stems[i]))
205 Real left = Stem::extremal_heads (stems[l])[d]
206 ->relative_coordinate (common, Y_AXIS);
207 Real right = Stem::extremal_heads (stems[i])[-d]
208 ->relative_coordinate (common, Y_AXIS);
210 Real dy = right - left;
214 knee_y = (right + left) / 2;
222 for (int i=0; i < stems.size (); i++)
224 if (Stem::invisible_b (stems[i]))
227 Real y = Stem::extremal_heads (stems[i])[d]
228 ->relative_coordinate (common, Y_AXIS);
230 Directional_element_interface::set (s, y < knee_y ? UP : DOWN);
231 s->set_grob_property ("dir-forced", SCM_BOOL_T);
238 Set stem's shorten property if unset.
240 take some y-position (chord/beam/nearest?) into account
241 scmify forced-fraction
244 Beam::set_stem_shorten (Grob*m)
246 Spanner*me = dynamic_cast<Spanner*> (m);
248 Real forced_fraction = forced_stem_count (me) / visible_stem_count (me);
249 if (forced_fraction < 0.5)
252 int multiplicity = get_multiplicity (me);
254 SCM shorten = me->get_grob_property ("beamed-stem-shorten");
255 if (shorten == SCM_EOL)
258 int sz = scm_ilength (shorten);
260 Real staff_space = Staff_symbol_referencer::staff_space (me);
261 SCM shorten_elt = scm_list_ref (shorten, gh_int2scm (multiplicity <? (sz - 1)));
262 Real shorten_f = gh_scm2double (shorten_elt) * staff_space;
264 /* cute, but who invented me -- how to customise ? */
265 if (forced_fraction < 1)
268 Link_array<Item> stems=
269 Pointer_group_interface__extract_elements (me, (Item*)0, "stems");
271 for (int i=0; i < stems.size (); i++)
274 if (Stem::invisible_b (s))
276 if (gh_number_p (s->get_grob_property ("shorten")))
277 s->set_grob_property ("shorten", gh_double2scm (shorten_f));
282 Call list of y-dy-callbacks, that handle setting of
283 grob-properties y, dy.
285 User may set grob-properties: y-position-hs and height-hs
286 (to be fixed) that override the calculated y and dy.
288 Because y and dy cannot be calculated and quanted separately, we
289 always calculate both, then check for user override.
291 MAKE_SCHEME_CALLBACK (Beam, after_line_breaking, 1);
293 Beam::after_line_breaking (SCM smob)
295 Grob * me = unsmob_grob (smob);
297 me->set_grob_property ("y", gh_double2scm (0));
298 me->set_grob_property ("dy", gh_double2scm (0));
300 /* Hmm, callbacks should be called by, a eh, callback mechanism
301 somewhere (?), I guess, not by looping here. */
303 SCM list = me->get_grob_property ("y-dy-callbacks");
304 for (SCM i = list; gh_pair_p (i); i = ly_cdr (i))
305 gh_call1 (ly_car (i), smob);
307 // UGH. Y is not in staff position unit?
308 // Ik dacht datwe daar juist van weg wilden?
310 // Hmm, nu hebben we 3 dimensies, want inmiddels zijn we daar
311 // weer terug, maar dan / 2
312 // (staff-space iso staff-position)
314 set_stem_lengths (me);
316 return SCM_UNSPECIFIED;
320 MAKE_SCHEME_CALLBACK (Beam, least_squares, 1);
322 Beam::least_squares (SCM smob)
324 Grob *me = unsmob_grob (smob);
326 if (visible_stem_count (me) <= 1)
327 return SCM_UNSPECIFIED;
332 /* Stem_info, and thus y,dy in this function are corrected for beam-dir */
333 Real first_ideal = Stem::calc_stem_info (first_visible_stem (me)).idealy_f_;
334 if (first_ideal == Stem::calc_stem_info (last_visible_stem (me)).idealy_f_)
341 Array<Offset> ideals;
343 // ugh -> use commonx
344 Real x0 = first_visible_stem (me)->relative_coordinate (0, X_AXIS);
345 Link_array<Item> stems=
346 Pointer_group_interface__extract_elements (me, (Item*)0, "stems");
348 for (int i=0; i < stems.size (); i++)
351 if (Stem::invisible_b (s))
353 ideals.push (Offset (s->relative_coordinate (0, X_AXIS) - x0,
354 Stem::calc_stem_info (s).idealy_f_));
357 minimise_least_squares (&dydx, &y, ideals);
359 Real dx = last_visible_stem (me)->relative_coordinate (0, X_AXIS) - x0;
363 /* Store true, not dir-corrected values */
364 Direction dir = Directional_element_interface::get (me);
365 me->set_grob_property ("y", gh_double2scm (y * dir));
366 me->set_grob_property ("dy", gh_double2scm (dy * dir));
367 return SCM_UNSPECIFIED;
370 MAKE_SCHEME_CALLBACK (Beam, cancel_suspect_slope, 1);
372 Beam::cancel_suspect_slope (SCM smob)
374 Grob *me = unsmob_grob (smob);
376 if (visible_stem_count (me) <= 1)
377 return SCM_UNSPECIFIED;
379 /* Stem_info, and thus y,dy in this function are corrected for beam-dir */
380 Direction dir = Directional_element_interface::get (me);
381 Real y = gh_scm2double (me->get_grob_property ("y")) * dir;
382 Real dy = gh_scm2double (me->get_grob_property ("dy")) * dir;
384 /* steep slope running against lengthened stem is suspect */
385 Real first_ideal = Stem::calc_stem_info (first_visible_stem (me)).idealy_f_;
386 Real last_ideal = Stem::calc_stem_info (last_visible_stem (me)).idealy_f_;
387 Real lengthened = gh_scm2double (me->get_grob_property ("outer-stem-length-limit"));
388 Real steep = gh_scm2double (me->get_grob_property ("slope-limit"));
390 // ugh -> use commonx
391 Real dx = last_visible_stem (me)->relative_coordinate (0, X_AXIS) - first_visible_stem (me)->relative_coordinate (0, X_AXIS);
392 Real dydx = dy && dx ? dy/dx : 0;
394 if (( (y - first_ideal > lengthened) && (dydx > steep))
395 || ((y + dy - last_ideal > lengthened) && (dydx < -steep)))
397 Real adjusted_y = y + dy / 2;
398 /* Store true, not dir-corrected values */
399 me->set_grob_property ("y", gh_double2scm (adjusted_y * dir));
400 me->set_grob_property ("dy", gh_double2scm (0));
402 return SCM_UNSPECIFIED;
406 This neat trick is by Werner Lemberg,
407 damped = tanh (slope)
408 corresponds with some tables in [Wanske]
410 MAKE_SCHEME_CALLBACK (Beam, slope_damping, 1);
412 Beam::slope_damping (SCM smob)
414 Grob *me = unsmob_grob (smob);
416 if (visible_stem_count (me) <= 1)
417 return SCM_UNSPECIFIED;
419 SCM s = me->get_grob_property ("damping");
420 int damping = gh_scm2int (s);
424 /* y,dy in this function are corrected for beam-dir */
425 Direction dir = Directional_element_interface::get (me);
426 Real y = gh_scm2double (me->get_grob_property ("y")) * dir;
427 Real dy = gh_scm2double (me->get_grob_property ("dy")) * dir;
429 // ugh -> use commonx
430 Real dx = last_visible_stem (me)->relative_coordinate (0, X_AXIS)
431 - first_visible_stem (me)->relative_coordinate (0, X_AXIS);
432 Real dydx = dy && dx ? dy/dx : 0;
433 dydx = 0.6 * tanh (dydx) / damping;
435 Real damped_dy = dydx * dx;
436 Real adjusted_y = y + (dy - damped_dy) / 2;
437 /* Store true, not dir-corrected values */
438 me->set_grob_property ("y", gh_double2scm (adjusted_y * dir));
439 me->set_grob_property ("dy", gh_double2scm (damped_dy * dir));
441 return SCM_UNSPECIFIED;
445 Quantise dy (height) of beam.
446 Generalisation of [Ross].
448 MAKE_SCHEME_CALLBACK (Beam, quantise_dy, 1);
450 Beam::quantise_dy (SCM smob)
452 Grob *me = unsmob_grob (smob);
454 if (visible_stem_count (me) <= 1)
455 return SCM_UNSPECIFIED;
458 SCM proc = me->get_grob_property ("height-quants");
459 SCM quants = gh_call2 (proc, me->self_scm (),
460 gh_double2scm (me->paper_l ()->get_var ("stafflinethickness")
463 for (SCM s = quants; gh_pair_p (s); s = ly_cdr (s))
464 a.push (gh_scm2double (ly_car (s)));
468 /* y,dy in this function are corrected for beam-dir */
469 Direction dir = Directional_element_interface::get (me);
470 Real y = gh_scm2double (me->get_grob_property ("y")) * dir;
471 Real dy = gh_scm2double (me->get_grob_property ("dy")) * dir;
473 Real staff_space = Staff_symbol_referencer::staff_space (me);
475 Interval iv = quantise_iv (a, abs (dy)/staff_space) * staff_space;
476 Real q = (abs (dy) - iv[SMALLER] <= iv[BIGGER] - abs (dy))
480 Real quantised_dy = q * sign (dy);
481 Real adjusted_y = y + (dy - quantised_dy) / 2;
482 /* Store true, not dir-corrected values */
483 me->set_grob_property ("y", gh_double2scm (adjusted_y * dir));
484 me->set_grob_property ("dy", gh_double2scm (quantised_dy * dir));
486 return SCM_UNSPECIFIED;
489 /* It's tricky to have the user override y,dy directly, so we use this
490 translation func. Also, if our staff_space != 1 (smaller staff, eg),
491 user will expect staff-position to be discrete values. */
492 MAKE_SCHEME_CALLBACK (Beam, user_override, 1);
494 Beam::user_override (SCM smob)
496 Grob *me = unsmob_grob (smob);
497 Real staff_space = Staff_symbol_referencer::staff_space (me);
499 SCM s = me->get_grob_property ("staff-position");
502 Real y = gh_scm2double (s) * staff_space * 0.5;
503 me->set_grob_property ("y", gh_double2scm (y));
506 /* Name suggestions? Tilt, slope, vertical-* ? */
507 s = me->get_grob_property ("height");
510 Real dy = gh_scm2double (s) * staff_space * 0.5;
511 me->set_grob_property ("dy", gh_double2scm (dy));
514 return SCM_UNSPECIFIED;
518 Ugh, this must be last, after user_override
519 Assumes directionised y/dy.
521 MAKE_SCHEME_CALLBACK (Beam, do_quantise_y, 1);
523 Beam::do_quantise_y (SCM smob)
525 Grob *me = unsmob_grob (smob);
528 If the user set y-position, we shouldn't do quanting.
530 if (gh_number_p (me->get_grob_property ("y-position-hs")))
531 return SCM_UNSPECIFIED;
533 Real y = gh_scm2double (me->get_grob_property ("y"));
534 Real dy = gh_scm2double (me->get_grob_property ("dy"));
536 /* we can modify y, so we should quantise y */
537 Real half_space = Staff_symbol_referencer::staff_space (me) / 2;
538 Real y_shift = check_stem_length_f (me, y, dy);
540 y = quantise_y_f (me, y, dy, 0);
543 Hmm, this is a bit keyhole operation: we're passing `this' as a
544 parameter, and member vars as SCM properties. We should decide on
545 SCM/C/C++ boundary */
546 me->set_grob_property ("y", gh_double2scm (y));
547 set_stem_lengths (me);
548 y = gh_scm2double (me->get_grob_property ("y"));
550 y_shift = check_stem_length_f (me, y, dy);
552 if (y_shift > half_space / 4)
557 for significantly lengthened or shortened stems,
558 request quanting the other way.
561 if (abs (y_shift) > half_space / 2)
562 quant_dir = sign (y_shift) * Directional_element_interface::get (me);
563 y = quantise_y_f (me, y, dy, quant_dir);
566 me->set_grob_property ("y", gh_double2scm (y));
567 // me->set_grob_property ("dy", gh_double2scm (dy));
568 return SCM_UNSPECIFIED;
573 Beam::calc_stem_y_f (Grob*me,Item* s, Real y, Real dy)
575 int beam_multiplicity = get_multiplicity (me);
576 int stem_multiplicity = (Stem::flag_i (s) - 2) >? 0;
578 SCM space_proc = me->get_grob_property ("space-function");
579 SCM space = gh_call1 (space_proc, gh_int2scm (beam_multiplicity));
581 Real thick = gh_scm2double (me->get_grob_property ("thickness")) ;
582 Real interbeam_f = gh_scm2double (space) ;
584 // ugh -> use commonx
585 Real x0 = first_visible_stem (me)->relative_coordinate (0, X_AXIS);
586 Real dx = last_visible_stem (me)->relative_coordinate (0, X_AXIS) - x0;
587 Real stem_y = (dy && dx ? (s->relative_coordinate (0, X_AXIS) - x0) / dx * dy : 0) + y;
590 Direction dir = Directional_element_interface::get (me);
591 Direction sdir = Directional_element_interface::get (s);
597 * (thick / 2 + (beam_multiplicity - 1) * interbeam_f);
601 // huh, why not for first visible?
602 if (Staff_symbol_referencer::staff_symbol_l (s)
603 != Staff_symbol_referencer::staff_symbol_l (last_visible_stem (me)))
604 stem_y += Directional_element_interface::get (me)
605 * (beam_multiplicity - stem_multiplicity) * interbeam_f;
612 Beam::check_stem_length_f (Grob*me,Real y, Real dy)
616 Direction dir = Directional_element_interface::get (me);
618 Link_array<Item> stems=
619 Pointer_group_interface__extract_elements (me, (Item*)0, "stems");
621 for (int i=0; i < stems.size (); i++)
624 if (Stem::invisible_b (s))
627 Real stem_y = calc_stem_y_f (me, s, y, dy);
630 Stem_info info = Stem::calc_stem_info (s);
632 // if (0 > info.maxy_f_ - stem_y)
633 shorten = shorten <? info.maxy_f_ - stem_y;
634 // if (0 < info.miny_f_ - stem_y)
635 lengthen = lengthen >? info.miny_f_ - stem_y;
638 if (lengthen && shorten)
639 warning (_ ("weird beam vertical offset"));
641 /* when all stems are too short, normal stems win */
642 return dir * ((shorten) ? shorten : lengthen);
646 Hmm. At this time, beam position and slope are determined. Maybe,
647 stem directions and length should set to relative to the chord's
648 position of the beam. */
650 Beam::set_stem_lengths (Grob *me)
652 if (visible_stem_count (me) <= 1)
655 Real y = gh_scm2double (me->get_grob_property ("y"));
656 Real dy = gh_scm2double (me->get_grob_property ("dy"));
658 Real half_space = Staff_symbol_referencer::staff_space (me)/2;
659 Link_array<Item> stems=
660 Pointer_group_interface__extract_elements (me, (Item*)0, "stems");
662 Grob *common = me->common_refpoint (stems[0], Y_AXIS);
663 for (int i=1; i < stems.size (); i++)
664 if (!Stem::invisible_b (stems[i]))
665 common = common->common_refpoint (stems[i], Y_AXIS);
667 for (int i=0; i < stems.size (); i++)
670 if (Stem::invisible_b (s))
673 Real stem_y = calc_stem_y_f (me, s, y, dy);
675 // doesn't play well with dvips
676 if (scm_definedp (ly_symbol2scm ("ps-testing"), SCM_UNDEFINED)
678 if (Stem::get_direction (s) == Directional_element_interface::get (me))
679 stem_y += Stem::get_direction (s)
680 * gh_scm2double (me->get_grob_property ("thickness")) / 2;
682 /* caution: stem measures in staff-positions */
683 Real id = me->relative_coordinate (common, Y_AXIS)
684 - stems[i]->relative_coordinate (common, Y_AXIS);
685 Stem::set_stemend (s, (stem_y + id) / half_space);
690 Prevent interference from stafflines and beams.
692 We only need to quantise the (left) y of the beam,
693 since dy is quantised too.
694 if extend_b then stems must *not* get shorter
697 Beam::quantise_y_f (Grob*me,Real y, Real dy, int quant_dir)
699 int multiplicity = get_multiplicity (me);
701 Real staff_space = Staff_symbol_referencer::staff_space (me);
702 Real thick = me->paper_l ()->get_var ("stafflinethickness");
705 SCM proc = me->get_grob_property ("vertical-position-quant-function");
706 SCM quants = scm_apply (proc,
708 scm_list_n (gh_int2scm (multiplicity),
709 gh_double2scm (dy/staff_space),
710 gh_double2scm (thick/staff_space),
711 SCM_EOL, SCM_UNDEFINED));
715 for (; gh_pair_p (quants); quants = ly_cdr (quants))
716 a.push (gh_scm2double (ly_car (quants)));
721 Real up_y = Directional_element_interface::get (me) * y;
722 Interval iv = quantise_iv (a, up_y/staff_space) * staff_space;
724 Real q = up_y - iv[SMALLER] <= iv[BIGGER] - up_y
725 ? iv[SMALLER] : iv[BIGGER];
727 q = iv[ (Direction)quant_dir];
729 return q * Directional_element_interface::get (me);
733 Beam::set_beaming (Grob*me,Beaming_info_list *beaming)
735 Link_array<Grob> stems=
736 Pointer_group_interface__extract_elements (me, (Grob*)0, "stems");
739 for (int i=0; i < stems.size (); i++)
743 /* Don't overwrite user override (?) */
744 if (Stem::beam_count (stems[i], d) == 0
745 /* Don't set beaming for outside of outer stems */
746 && ! (d == LEFT && i == 0)
747 && ! (d == RIGHT && i == stems.size () -1))
749 int b = beaming->infos_.elem (i).beams_i_drul_[d];
750 Stem::set_beaming (stems[i], b, d);
753 while (flip (&d) != LEFT);
760 beams to go with one stem.
765 Beam::stem_beams (Grob*me,Item *here, Item *next, Item *prev,
766 Real /* dy */ , Real dydx
769 // ugh -> use commonx
770 if ((next && ! (next->relative_coordinate (0, X_AXIS) > here->relative_coordinate (0, X_AXIS))) ||
771 (prev && ! (prev->relative_coordinate (0, X_AXIS) < here->relative_coordinate (0, X_AXIS))))
772 programming_error ("Beams are not left-to-right");
774 int multiplicity = get_multiplicity (me);
776 SCM space_proc = me->get_grob_property ("space-function");
777 SCM space = gh_call1 (space_proc, gh_int2scm (multiplicity));
779 Real thick = gh_scm2double (me->get_grob_property ("thickness")) ;
780 Real interbeam_f = gh_scm2double (space) ;
782 Real bdy = interbeam_f;
785 // ugh -> use commonx
786 Real dx = visible_stem_count (me) ?
787 last_visible_stem (me)->relative_coordinate (0, X_AXIS) - first_visible_stem (me)->relative_coordinate (0, X_AXIS)
795 if (!Stem::first_head (here))
798 int t = Stem::type_i (here);
800 SCM proc = me->get_grob_property ("flag-width-function");
801 SCM result = gh_call1 (proc, gh_int2scm (t));
802 nw_f = gh_scm2double (result);
806 Direction dir = Directional_element_interface::get (me);
808 /* [Tremolo] beams on whole notes may not have direction set? */
810 dir = Directional_element_interface::get (here);
813 /* half beams extending to the left. */
816 int lhalfs= lhalfs = Stem::beam_count (here,LEFT) - Stem::beam_count (prev,RIGHT);
817 int lwholebeams= Stem::beam_count (here,LEFT) <? Stem::beam_count (prev,RIGHT) ;
819 Half beam should be one note-width,
820 but let's make sure two half-beams never touch
823 // FIXME: TODO (check) stem width / sloped beams
824 Real w = here->relative_coordinate (0, X_AXIS)
825 - prev->relative_coordinate (0, X_AXIS);
826 Real stem_w = gh_scm2double (prev->get_grob_property ("thickness"))
828 * me->paper_l ()->get_var ("stafflinethickness");
832 if (lhalfs) // generates warnings if not
833 a = Lookup::beam (dydx, w + stem_w, thick);
834 a.translate (Offset (-w, -w * dydx));
835 a.translate_axis (-stem_w/2, X_AXIS);
836 for (int j = 0; j < lhalfs; j++)
839 b.translate_axis (-dir * bdy * (lwholebeams+j), Y_AXIS);
840 leftbeams.add_molecule (b);
846 int rhalfs = Stem::beam_count (here,RIGHT)
847 - Stem::beam_count (next,LEFT);
848 int rwholebeams= Stem::beam_count (here,RIGHT)
849 <? Stem::beam_count (next,LEFT) ;
851 Real w = next->relative_coordinate (0, X_AXIS)
852 - here->relative_coordinate (0, X_AXIS);
854 Real stem_w = gh_scm2double (next->get_grob_property ("thickness"))
856 * me->paper_l ()->get_var ("stafflinethickness");
858 Molecule a = Lookup::beam (dydx, w + stem_w, thick);
859 a.translate_axis (- stem_w/2, X_AXIS);
863 SCM gap = me->get_grob_property ("gap");
864 if (gh_number_p (gap))
866 int gap_i = gh_scm2int ((gap));
867 int nogap = rwholebeams - gap_i;
869 for (; j < nogap; j++)
872 b.translate_axis (-dir * bdy * j, Y_AXIS);
873 rightbeams.add_molecule (b);
875 if (Stem::invisible_b (here))
880 a = Lookup::beam (dydx, w + stem_w, thick);
883 for (; j < rwholebeams; j++)
887 if (Stem::invisible_b (here))
888 // ugh, see chord-tremolo.ly
889 tx = (-dir + 1) / 2 * nw_f * 1.5 + gap_f/4;
892 b.translate (Offset (tx, -dir * bdy * j));
893 rightbeams.add_molecule (b);
898 a = Lookup::beam (dydx, w, thick);
900 for (; j < rwholebeams + rhalfs; j++)
903 b.translate_axis (- dir * bdy * j, Y_AXIS);
904 rightbeams.add_molecule (b);
908 leftbeams.add_molecule (rightbeams);
911 Does beam quanting think of the asymetry of beams?
912 Refpoint is on bottom of symbol. (FIXTHAT) --hwn.
917 MAKE_SCHEME_CALLBACK (Beam,brew_molecule,1);
919 Beam::brew_molecule (SCM smob)
921 Grob * me =unsmob_grob (smob);
924 if (!gh_pair_p (me->get_grob_property ("stems")))
927 Link_array<Item>stems =
928 Pointer_group_interface__extract_elements (me, (Item*) 0, "stems");
929 if (visible_stem_count (me))
931 // ugh -> use commonx
932 x0 = first_visible_stem (me)->relative_coordinate (0, X_AXIS);
933 dx = last_visible_stem (me)->relative_coordinate (0, X_AXIS) - x0;
937 x0 = stems[0]->relative_coordinate (0, X_AXIS);
938 dx = stems.top ()->relative_coordinate (0, X_AXIS) - x0;
944 TODO: the naming of the grob properties sucks.
946 SCM dy_s = me->get_grob_property ("dy");
947 SCM y_s = me->get_grob_property ("y");
950 Real dy = gh_number_p (dy_s) ? gh_scm2double (dy_s) : 0.0;
951 Real dydx = dy && dx ? dy/dx : 0;
952 Real y = gh_number_p (y_s) ? gh_scm2double (y_s) : 0.0;
955 for (int j=0; j <stems.size (); j++)
958 Item * prev = (j > 0)? stems[j-1] : 0;
959 Item * next = (j < stems.size ()-1) ? stems[j+1] :0;
961 Molecule sb = stem_beams (me, i, next, prev, dy, dydx);
962 Real x = i->relative_coordinate (0, X_AXIS)-x0;
963 sb.translate (Offset (x, x * dydx + y));
964 mol.add_molecule (sb);
966 mol.translate_axis (x0
967 - dynamic_cast<Spanner*> (me)->get_bound (LEFT)->relative_coordinate (0, X_AXIS), X_AXIS);
969 return mol.smobbed_copy ();
973 Beam::forced_stem_count (Grob*me)
975 Link_array<Item>stems =
976 Pointer_group_interface__extract_elements (me, (Item*) 0, "stems");
978 for (int i=0; i < stems.size (); i++)
982 if (Stem::invisible_b (s))
985 if (( (int)Stem::chord_start_f (s))
986 && (Stem::get_direction (s) != Stem::get_default_dir (s)))
996 use filter and standard list functions.
999 Beam::visible_stem_count (Grob*me)
1001 Link_array<Item>stems =
1002 Pointer_group_interface__extract_elements (me, (Item*) 0, "stems");
1004 for (int i = stems.size (); i--;)
1006 if (!Stem::invisible_b (stems[i]))
1013 Beam::first_visible_stem (Grob*me)
1015 Link_array<Item>stems =
1016 Pointer_group_interface__extract_elements (me, (Item*) 0, "stems");
1018 for (int i = 0; i < stems.size (); i++)
1020 if (!Stem::invisible_b (stems[i]))
1027 Beam::last_visible_stem (Grob*me)
1029 Link_array<Item>stems =
1030 Pointer_group_interface__extract_elements (me, (Item*) 0, "stems");
1031 for (int i = stems.size (); i--;)
1033 if (!Stem::invisible_b (stems[i]))
1042 handle rest under beam (do_post: beams are calculated now)
1043 what about combination of collisions and rest under beam.
1047 rest -> stem -> beam -> interpolate_y_position ()
1049 MAKE_SCHEME_CALLBACK (Beam,rest_collision_callback,2);
1051 Beam::rest_collision_callback (SCM element_smob, SCM axis)
1053 Grob *rest = unsmob_grob (element_smob);
1054 Axis a = (Axis) gh_scm2int (axis);
1056 assert (a == Y_AXIS);
1058 Grob * st = unsmob_grob (rest->get_grob_property ("stem"));
1061 return gh_double2scm (0.0);
1062 Grob * beam = unsmob_grob (stem->get_grob_property ("beam"));
1063 if (!beam || !Beam::has_interface (beam) || !Beam::visible_stem_count (beam))
1064 return gh_double2scm (0.0);
1066 // make callback for rest from this.
1071 // todo: make sure this calced already.
1072 SCM s = beam->get_grob_property ("dy");
1073 if (gh_number_p (s))
1074 beam_dy = gh_scm2double (s);
1076 s = beam->get_grob_property ("y");
1077 if (gh_number_p (s))
1078 beam_y = gh_scm2double (s);
1080 // ugh -> use commonx
1081 Real x0 = first_visible_stem (beam)->relative_coordinate (0, X_AXIS);
1082 Real dx = last_visible_stem (beam)->relative_coordinate (0, X_AXIS) - x0;
1083 Real dydx = beam_dy && dx ? beam_dy/dx : 0;
1085 Direction d = Stem::get_direction (stem);
1086 Real beamy = (stem->relative_coordinate (0, X_AXIS) - x0) * dydx + beam_y;
1088 Real staff_space = Staff_symbol_referencer::staff_space (rest);
1091 Real rest_dim = rest->extent (rest, Y_AXIS)[d]*2.0 / staff_space ; // refp??
1094 = gh_scm2double (rest->get_grob_property ("minimum-beam-collision-distance"));
1096 minimum_dist + -d * (beamy - rest_dim) >? 0;
1098 int stafflines = Staff_symbol_referencer::line_count (rest);
1100 // move discretely by half spaces.
1101 int discrete_dist = int (ceil (dist));
1103 // move by whole spaces inside the staff.
1104 if (discrete_dist < stafflines+1)
1105 discrete_dist = int (ceil (discrete_dist / 2.0)* 2.0);
1107 return gh_double2scm (-d * discrete_dist);
1112 Beam::has_interface (Grob*me)
1114 return me->has_interface (ly_symbol2scm ("beam-interface"));