lilypond-1.3.145
[lilypond.git] / lily / beam.cc
blob901d56ab21bbb24f1e04b63c9745dbeacdf84ed8
1 /*
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>
9 */
12 [TODO]
14 * shorter! (now +- 1000 lines)
15 * less hairy code
19 #include <math.h> // tanh.
21 #include "molecule.hh"
22 #include "directional-element-interface.hh"
23 #include "beaming.hh"
24 #include "beam.hh"
25 #include "misc.hh"
26 #include "least-squares.hh"
27 #include "stem.hh"
28 #include "paper-def.hh"
29 #include "lookup.hh"
30 #include "group-interface.hh"
31 #include "staff-symbol-referencer.hh"
32 #include "item.hh"
33 #include "spanner.hh"
34 #include "warn.hh"
36 void
37 Beam::add_stem (Grob*me, Grob*s)
39 Pointer_group_interface:: add_element (me, "stems", s);
41 s->add_dependency (me);
43 assert (!Stem::beam_l (s));
44 s->set_grob_property ("beam", me->self_scm ());
46 add_bound_item (dynamic_cast<Spanner*> (me), dynamic_cast<Item*> (s));
49 int
50 Beam::get_multiplicity (Grob*me)
52 int m = 0;
53 for (SCM s = me->get_grob_property ("stems"); gh_pair_p (s); s = gh_cdr (s))
55 Grob * sc = unsmob_grob (gh_car (s));
57 if (Stem::has_interface (sc))
58 m = m >? Stem::beam_count (sc,LEFT) >? Stem::beam_count (sc,RIGHT);
60 return m;
64 After pre-processing all directions should be set.
65 Several post-processing routines (stem, slur, script) need stem/beam
66 direction.
67 Currenly, this means that beam has set all stem's directions.
68 [Alternatively, stems could set its own directions, according to
69 their beam, during 'final-pre-processing'.]
71 MAKE_SCHEME_CALLBACK (Beam,before_line_breaking,1);
72 SCM
73 Beam::before_line_breaking (SCM smob)
75 Grob * me = unsmob_grob (smob);
77 // Why?
79 Why what? Why the warning (beams with less than 2 stems are
80 degenerate beams, should never happen), or why would this ever
81 happen (don't know). */
82 if (visible_stem_count (me) < 2)
84 warning (_ ("beam has less than two stems"));
86 if (visible_stem_count (me) >= 1)
88 if (!Directional_element_interface::get (me))
89 Directional_element_interface::set (me, get_default_dir (me));
91 consider_auto_knees (me);
92 set_stem_directions (me);
93 set_stem_shorten (me);
95 return SCM_EOL;
98 Direction
99 Beam::get_default_dir (Grob*me)
101 Drul_array<int> total;
102 total[UP] = total[DOWN] = 0;
103 Drul_array<int> count;
104 count[UP] = count[DOWN] = 0;
105 Direction d = DOWN;
107 Link_array<Item> stems=
108 Pointer_group_interface__extract_elements (me, (Item*)0, "stems");
110 for (int i=0; i <stems.size (); i++)
111 do {
112 Grob *s = stems[i];
113 Direction sd = Directional_element_interface::get (s);
114 int current = sd ? (1 + d * sd)/2
115 : Stem::get_center_distance (s, (Direction)-d);
117 if (current)
119 total[d] += current;
120 count[d] ++;
123 } while (flip (&d) != DOWN);
125 SCM func = me->get_grob_property ("dir-function");
126 SCM s = gh_call2 (func,
127 gh_cons (gh_int2scm (count[UP]),
128 gh_int2scm (count[DOWN])),
129 gh_cons (gh_int2scm (total[UP]),
130 gh_int2scm (total[DOWN])));
132 if (gh_number_p (s) && gh_scm2int (s))
133 return to_dir (s);
136 If dir is not determined: get default
138 return to_dir (me->get_grob_property ("default-neutral-direction"));
143 Set all stems with non-forced direction to beam direction.
144 Urg: non-forced should become `without/with unforced' direction,
145 once stem gets cleaned-up.
147 void
148 Beam::set_stem_directions (Grob*me)
150 Link_array<Item> stems
151 =Pointer_group_interface__extract_elements (me, (Item*) 0, "stems");
152 Direction d = Directional_element_interface::get (me);
154 for (int i=0; i <stems.size (); i++)
156 Grob *s = stems[i];
157 SCM force = s->remove_grob_property ("dir-forced");
158 if (!gh_boolean_p (force) || !gh_scm2bool (force))
159 Directional_element_interface ::set (s,d);
164 Simplistic auto-knees; only consider vertical gap between two
165 adjacent chords.
167 `Forced' stem directions are ignored. If you don't want auto-knees,
168 don't set, or unset auto-knee-gap.
170 void
171 Beam::consider_auto_knees (Grob *me)
173 SCM scm = me->get_grob_property ("auto-knee-gap");
175 if (gh_number_p (scm))
177 bool knee_b = false;
178 Real knee_y = 0;
179 Real staff_space = Staff_symbol_referencer::staff_space (me);
180 Real gap = gh_scm2double (scm) / staff_space;
182 Direction d = Directional_element_interface::get (me);
183 Link_array<Item> stems=
184 Pointer_group_interface__extract_elements (me, (Item*)0, "stems");
186 Grob *common = me->common_refpoint (stems[0], Y_AXIS);
187 for (int i=1; i < stems.size (); i++)
188 if (!Stem::invisible_b (stems[i]))
189 common = common->common_refpoint (stems[i], Y_AXIS);
191 int l = 0;
192 for (int i=1; i < stems.size (); i++)
194 if (!Stem::invisible_b (stems[i-1]))
195 l = i - 1;
196 if (Stem::invisible_b (stems[l]))
197 continue;
198 if (Stem::invisible_b (stems[i]))
199 continue;
201 Real left = Stem::extremal_heads (stems[l])[d]
202 ->relative_coordinate (common, Y_AXIS);
203 Real right = Stem::extremal_heads (stems[i])[-d]
204 ->relative_coordinate (common, Y_AXIS);
206 Real dy = right - left;
208 if (abs (dy) >= gap)
210 knee_y = (right + left) / 2;
211 knee_b = true;
212 break;
216 if (knee_b)
218 for (int i=0; i < stems.size (); i++)
220 if (Stem::invisible_b (stems[i]))
221 continue;
222 Item *s = stems[i];
223 Real y = Stem::extremal_heads (stems[i])[d]
224 ->relative_coordinate (common, Y_AXIS);
226 Directional_element_interface::set (s, y < knee_y ? UP : DOWN);
227 s->set_grob_property ("dir-forced", SCM_BOOL_T);
234 Set stem's shorten property if unset.
235 TODO:
236 take some y-position (chord/beam/nearest?) into account
237 scmify forced-fraction
239 void
240 Beam::set_stem_shorten (Grob*m)
242 Spanner*me = dynamic_cast<Spanner*> (m);
244 Real forced_fraction = forced_stem_count (me) / visible_stem_count (me);
245 if (forced_fraction < 0.5)
246 return;
248 int multiplicity = get_multiplicity (me);
250 SCM shorten = me->get_grob_property ("beamed-stem-shorten");
251 if (shorten == SCM_EOL)
252 return;
254 int sz = scm_ilength (shorten);
256 Real staff_space = Staff_symbol_referencer::staff_space (me);
257 SCM shorten_elt = scm_list_ref (shorten, gh_int2scm (multiplicity <? (sz - 1)));
258 Real shorten_f = gh_scm2double (shorten_elt) * staff_space;
260 /* cute, but who invented me -- how to customise ? */
261 if (forced_fraction < 1)
262 shorten_f /= 2;
264 Link_array<Item> stems=
265 Pointer_group_interface__extract_elements (me, (Item*)0, "stems");
267 for (int i=0; i < stems.size (); i++)
269 Item* s = stems[i];
270 if (Stem::invisible_b (s))
271 continue;
272 if (gh_number_p (s->get_grob_property ("shorten")))
273 s->set_grob_property ("shorten", gh_double2scm (shorten_f));
278 Call list of y-dy-callbacks, that handle setting of
279 grob-properties y, dy.
281 User may set grob-properties: y-position-hs and height-hs
282 (to be fixed) that override the calculated y and dy.
284 Because y and dy cannot be calculated and quanted separately, we
285 always calculate both, then check for user override.
287 MAKE_SCHEME_CALLBACK (Beam, after_line_breaking, 1);
289 Beam::after_line_breaking (SCM smob)
291 Grob * me = unsmob_grob (smob);
293 me->set_grob_property ("y", gh_double2scm (0));
294 me->set_grob_property ("dy", gh_double2scm (0));
296 /* Hmm, callbacks should be called by, a eh, callback mechanism
297 somewhere (?), I guess, not by looping here. */
299 SCM list = me->get_grob_property ("y-dy-callbacks");
300 for (SCM i = list; gh_pair_p (i); i = gh_cdr (i))
301 gh_call1 (gh_car (i), smob);
303 // UGH. Y is not in staff position unit?
304 // Ik dacht datwe daar juist van weg wilden?
306 // Hmm, nu hebben we 3 dimensies, want inmiddels zijn we daar
307 // weer terug, maar dan / 2
308 // (staff-space iso staff-position)
310 set_stem_lengths (me);
312 return SCM_UNSPECIFIED;
316 MAKE_SCHEME_CALLBACK (Beam, least_squares, 1);
318 Beam::least_squares (SCM smob)
320 Grob *me = unsmob_grob (smob);
322 if (visible_stem_count (me) <= 1)
323 return SCM_UNSPECIFIED;
325 Real y = 0;
326 Real dy = 0;
328 /* Stem_info, and thus y,dy in this function are corrected for beam-dir */
329 Real first_ideal = Stem::calc_stem_info (first_visible_stem (me)).idealy_f_;
330 if (first_ideal == Stem::calc_stem_info (last_visible_stem (me)).idealy_f_)
332 y = first_ideal;
333 dy = 0;
335 else
337 Array<Offset> ideals;
339 // ugh -> use commonx
340 Real x0 = first_visible_stem (me)->relative_coordinate (0, X_AXIS);
341 Link_array<Item> stems=
342 Pointer_group_interface__extract_elements (me, (Item*)0, "stems");
344 for (int i=0; i < stems.size (); i++)
346 Item* s = stems[i];
347 if (Stem::invisible_b (s))
348 continue;
349 ideals.push (Offset (s->relative_coordinate (0, X_AXIS) - x0,
350 Stem::calc_stem_info (s).idealy_f_));
352 Real dydx;
353 minimise_least_squares (&dydx, &y, ideals);
355 Real dx = last_visible_stem (me)->relative_coordinate (0, X_AXIS) - x0;
356 dy = dydx * dx;
359 /* Store true, not dir-corrected values */
360 Direction dir = Directional_element_interface::get (me);
361 me->set_grob_property ("y", gh_double2scm (y * dir));
362 me->set_grob_property ("dy", gh_double2scm (dy * dir));
363 return SCM_UNSPECIFIED;
366 MAKE_SCHEME_CALLBACK (Beam, cancel_suspect_slope, 1);
368 Beam::cancel_suspect_slope (SCM smob)
370 Grob *me = unsmob_grob (smob);
372 if (visible_stem_count (me) <= 1)
373 return SCM_UNSPECIFIED;
375 /* Stem_info, and thus y,dy in this function are corrected for beam-dir */
376 Direction dir = Directional_element_interface::get (me);
377 Real y = gh_scm2double (me->get_grob_property ("y")) * dir;
378 Real dy = gh_scm2double (me->get_grob_property ("dy")) * dir;
380 /* steep slope running against lengthened stem is suspect */
381 Real first_ideal = Stem::calc_stem_info (first_visible_stem (me)).idealy_f_;
382 Real last_ideal = Stem::calc_stem_info (last_visible_stem (me)).idealy_f_;
383 Real lengthened = gh_scm2double (me->get_grob_property ("outer-stem-length-limit"));
384 Real steep = gh_scm2double (me->get_grob_property ("slope-limit"));
386 // ugh -> use commonx
387 Real dx = last_visible_stem (me)->relative_coordinate (0, X_AXIS) - first_visible_stem (me)->relative_coordinate (0, X_AXIS);
388 Real dydx = dy && dx ? dy/dx : 0;
390 if (( (y - first_ideal > lengthened) && (dydx > steep))
391 || ((y + dy - last_ideal > lengthened) && (dydx < -steep)))
393 Real adjusted_y = y + dy / 2;
394 /* Store true, not dir-corrected values */
395 me->set_grob_property ("y", gh_double2scm (adjusted_y * dir));
396 me->set_grob_property ("dy", gh_double2scm (0));
398 return SCM_UNSPECIFIED;
402 This neat trick is by Werner Lemberg,
403 damped = tanh (slope)
404 corresponds with some tables in [Wanske]
406 MAKE_SCHEME_CALLBACK (Beam, slope_damping, 1);
408 Beam::slope_damping (SCM smob)
410 Grob *me = unsmob_grob (smob);
412 if (visible_stem_count (me) <= 1)
413 return SCM_UNSPECIFIED;
415 SCM s = me->get_grob_property ("damping");
416 int damping = gh_scm2int (s);
418 if (damping)
420 /* y,dy in this function are corrected for beam-dir */
421 Direction dir = Directional_element_interface::get (me);
422 Real y = gh_scm2double (me->get_grob_property ("y")) * dir;
423 Real dy = gh_scm2double (me->get_grob_property ("dy")) * dir;
425 // ugh -> use commonx
426 Real dx = last_visible_stem (me)->relative_coordinate (0, X_AXIS)
427 - first_visible_stem (me)->relative_coordinate (0, X_AXIS);
428 Real dydx = dy && dx ? dy/dx : 0;
429 dydx = 0.6 * tanh (dydx) / damping;
431 Real damped_dy = dydx * dx;
432 Real adjusted_y = y + (dy - damped_dy) / 2;
433 /* Store true, not dir-corrected values */
434 me->set_grob_property ("y", gh_double2scm (adjusted_y * dir));
435 me->set_grob_property ("dy", gh_double2scm (damped_dy * dir));
437 return SCM_UNSPECIFIED;
441 Quantise dy (height) of beam.
442 Generalisation of [Ross].
444 MAKE_SCHEME_CALLBACK (Beam, quantise_dy, 1);
446 Beam::quantise_dy (SCM smob)
448 Grob *me = unsmob_grob (smob);
450 if (visible_stem_count (me) <= 1)
451 return SCM_UNSPECIFIED;
453 Array<Real> a;
454 SCM proc = me->get_grob_property ("height-quants");
455 SCM quants = gh_call2 (proc, me->self_scm (),
456 gh_double2scm (me->paper_l ()->get_var ("stafflinethickness")
457 / 1.0));
459 for (SCM s = quants; gh_pair_p (s); s = gh_cdr (s))
460 a.push (gh_scm2double (gh_car (s)));
462 if (a.size () > 1)
464 /* y,dy in this function are corrected for beam-dir */
465 Direction dir = Directional_element_interface::get (me);
466 Real y = gh_scm2double (me->get_grob_property ("y")) * dir;
467 Real dy = gh_scm2double (me->get_grob_property ("dy")) * dir;
469 Real staff_space = Staff_symbol_referencer::staff_space (me);
471 Interval iv = quantise_iv (a, abs (dy)/staff_space) * staff_space;
472 Real q = (abs (dy) - iv[SMALLER] <= iv[BIGGER] - abs (dy))
473 ? iv[SMALLER]
474 : iv[BIGGER];
476 Real quantised_dy = q * sign (dy);
477 Real adjusted_y = y + (dy - quantised_dy) / 2;
478 /* Store true, not dir-corrected values */
479 me->set_grob_property ("y", gh_double2scm (adjusted_y * dir));
480 me->set_grob_property ("dy", gh_double2scm (quantised_dy * dir));
482 return SCM_UNSPECIFIED;
485 /* It's tricky to have the user override y,dy directly, so we use this
486 translation func. Also, if our staff_space != 1 (smaller staff, eg),
487 user will expect staff-position to be discrete values. */
488 MAKE_SCHEME_CALLBACK (Beam, user_override, 1);
490 Beam::user_override (SCM smob)
492 Grob *me = unsmob_grob (smob);
493 Real staff_space = Staff_symbol_referencer::staff_space (me);
495 SCM s = me->get_grob_property ("staff-position");
496 if (gh_number_p (s))
498 Real y = gh_scm2double (s) * staff_space;
499 me->set_grob_property ("y", gh_double2scm (y));
502 /* Name suggestions? Tilt, slope, vertical-* ? */
503 s = me->get_grob_property ("height");
504 if (gh_number_p (s))
506 Real dy = gh_scm2double (s) * staff_space;
507 me->set_grob_property ("dy", gh_double2scm (dy));
510 return SCM_UNSPECIFIED;
514 Ugh, this must be last, after user_override
515 Assumes directionised y/dy.
517 MAKE_SCHEME_CALLBACK (Beam, do_quantise_y, 1);
519 Beam::do_quantise_y (SCM smob)
521 Grob *me = unsmob_grob (smob);
524 If the user set y-position, we shouldn't do quanting.
526 if (gh_number_p (me->get_grob_property ("y-position-hs")))
527 return SCM_UNSPECIFIED;
529 Real y = gh_scm2double (me->get_grob_property ("y"));
530 Real dy = gh_scm2double (me->get_grob_property ("dy"));
532 /* we can modify y, so we should quantise y */
533 Real half_space = Staff_symbol_referencer::staff_space (me) / 2;
534 Real y_shift = check_stem_length_f (me, y, dy);
535 y += y_shift;
536 y = quantise_y_f (me, y, dy, 0);
539 Hmm, this is a bit keyhole operation: we're passing `this' as a
540 parameter, and member vars as SCM properties. We should decide on
541 SCM/C/C++ boundary */
542 me->set_grob_property ("y", gh_double2scm (y));
543 set_stem_lengths (me);
544 y = gh_scm2double (me->get_grob_property ("y"));
546 y_shift = check_stem_length_f (me, y, dy);
548 if (y_shift > half_space / 4)
550 y += y_shift;
553 for significantly lengthened or shortened stems,
554 request quanting the other way.
556 int quant_dir = 0;
557 if (abs (y_shift) > half_space / 2)
558 quant_dir = sign (y_shift) * Directional_element_interface::get (me);
559 y = quantise_y_f (me, y, dy, quant_dir);
562 me->set_grob_property ("y", gh_double2scm (y));
563 // me->set_grob_property ("dy", gh_double2scm (dy));
564 return SCM_UNSPECIFIED;
568 Real
569 Beam::calc_stem_y_f (Grob*me,Item* s, Real y, Real dy)
571 int beam_multiplicity = get_multiplicity (me);
572 int stem_multiplicity = (Stem::flag_i (s) - 2) >? 0;
574 SCM space_proc = me->get_grob_property ("space-function");
575 SCM space = gh_call1 (space_proc, gh_int2scm (beam_multiplicity));
577 Real thick = gh_scm2double (me->get_grob_property ("thickness")) ;
578 Real interbeam_f = gh_scm2double (space) ;
580 // ugh -> use commonx
581 Real x0 = first_visible_stem (me)->relative_coordinate (0, X_AXIS);
582 Real dx = last_visible_stem (me)->relative_coordinate (0, X_AXIS) - x0;
583 Real stem_y = (dy && dx ? (s->relative_coordinate (0, X_AXIS) - x0) / dx * dy : 0) + y;
585 /* knee */
586 Direction dir = Directional_element_interface::get (me);
587 Direction sdir = Directional_element_interface::get (s);
589 /* knee */
590 if (dir!= sdir)
592 stem_y -= dir
593 * (thick / 2 + (beam_multiplicity - 1) * interbeam_f);
597 // huh, why not for first visible?
598 if (Staff_symbol_referencer::staff_symbol_l (s)
599 != Staff_symbol_referencer::staff_symbol_l (last_visible_stem (me)))
600 stem_y += Directional_element_interface::get (me)
601 * (beam_multiplicity - stem_multiplicity) * interbeam_f;
604 return stem_y;
607 Real
608 Beam::check_stem_length_f (Grob*me,Real y, Real dy)
610 Real shorten = 0;
611 Real lengthen = 0;
612 Direction dir = Directional_element_interface::get (me);
614 Link_array<Item> stems=
615 Pointer_group_interface__extract_elements (me, (Item*)0, "stems");
617 for (int i=0; i < stems.size (); i++)
619 Item* s = stems[i];
620 if (Stem::invisible_b (s))
621 continue;
623 Real stem_y = calc_stem_y_f (me, s, y, dy);
625 stem_y *= dir;
626 Stem_info info = Stem::calc_stem_info (s);
628 // if (0 > info.maxy_f_ - stem_y)
629 shorten = shorten <? info.maxy_f_ - stem_y;
630 // if (0 < info.miny_f_ - stem_y)
631 lengthen = lengthen >? info.miny_f_ - stem_y;
634 if (lengthen && shorten)
635 warning (_ ("weird beam vertical offset"));
637 /* when all stems are too short, normal stems win */
638 return dir * ((shorten) ? shorten : lengthen);
642 Hmm. At this time, beam position and slope are determined. Maybe,
643 stem directions and length should set to relative to the chord's
644 position of the beam. */
645 void
646 Beam::set_stem_lengths (Grob *me)
648 if (visible_stem_count (me) <= 1)
649 return;
651 Real y = gh_scm2double (me->get_grob_property ("y"));
652 Real dy = gh_scm2double (me->get_grob_property ("dy"));
654 Real half_space = Staff_symbol_referencer::staff_space (me)/2;
655 Link_array<Item> stems=
656 Pointer_group_interface__extract_elements (me, (Item*)0, "stems");
658 Grob *common = me->common_refpoint (stems[0], Y_AXIS);
659 for (int i=1; i < stems.size (); i++)
660 if (!Stem::invisible_b (stems[i]))
661 common = common->common_refpoint (stems[i], Y_AXIS);
663 for (int i=0; i < stems.size (); i++)
665 Item* s = stems[i];
666 if (Stem::invisible_b (s))
667 continue;
669 Real stem_y = calc_stem_y_f (me, s, y, dy);
671 /* caution: stem measures in staff-positions */
672 Real id = me->relative_coordinate (common, Y_AXIS)
673 - stems[i]->relative_coordinate (common, Y_AXIS);
674 Stem::set_stemend (s, (stem_y + id) / half_space);
679 Prevent interference from stafflines and beams.
681 We only need to quantise the (left) y of the beam,
682 since dy is quantised too.
683 if extend_b then stems must *not* get shorter
685 Real
686 Beam::quantise_y_f (Grob*me,Real y, Real dy, int quant_dir)
688 int multiplicity = get_multiplicity (me);
690 Real staff_space = Staff_symbol_referencer::staff_space (me);
691 Real thick = me->paper_l ()->get_var ("stafflinethickness");
694 SCM proc = me->get_grob_property ("vertical-position-quant-function");
695 SCM quants = scm_apply (proc,
696 me->self_scm (),
697 gh_list (gh_int2scm (multiplicity),
698 gh_double2scm (dy/staff_space),
699 gh_double2scm (thick/staff_space),
700 SCM_EOL, SCM_UNDEFINED));
702 Array<Real> a;
704 for (; gh_pair_p (quants); quants = gh_cdr (quants))
705 a.push (gh_scm2double (gh_car (quants)));
707 if (a.size () <= 1)
708 return y;
710 Real up_y = Directional_element_interface::get (me) * y;
711 Interval iv = quantise_iv (a, up_y/staff_space) * staff_space;
713 Real q = up_y - iv[SMALLER] <= iv[BIGGER] - up_y
714 ? iv[SMALLER] : iv[BIGGER];
715 if (quant_dir)
716 q = iv[ (Direction)quant_dir];
718 return q * Directional_element_interface::get (me);
721 void
722 Beam::set_beaming (Grob*me,Beaming_info_list *beaming)
724 Link_array<Grob> stems=
725 Pointer_group_interface__extract_elements (me, (Grob*)0, "stems");
727 Direction d = LEFT;
728 for (int i=0; i < stems.size (); i++)
732 /* Don't overwrite user override (?) */
733 if (Stem::beam_count (stems[i], d) == 0
734 /* Don't set beaming for outside of outer stems */
735 && ! (d == LEFT && i == 0)
736 && ! (d == RIGHT && i == stems.size () -1))
738 int b = beaming->infos_.elem (i).beams_i_drul_[d];
739 Stem::set_beaming (stems[i], b, d);
742 while (flip (&d) != LEFT);
749 beams to go with one stem.
751 FIXME: clean me up.
753 Molecule
754 Beam::stem_beams (Grob*me,Item *here, Item *next, Item *prev,
755 Real dy, Real dydx
758 // ugh -> use commonx
759 if ((next && ! (next->relative_coordinate (0, X_AXIS) > here->relative_coordinate (0, X_AXIS))) ||
760 (prev && ! (prev->relative_coordinate (0, X_AXIS) < here->relative_coordinate (0, X_AXIS))))
761 programming_error ("Beams are not left-to-right");
763 Real staffline_f = me->paper_l ()->get_var ("stafflinethickness");
764 int multiplicity = get_multiplicity (me);
766 SCM space_proc = me->get_grob_property ("space-function");
767 SCM space = gh_call1 (space_proc, gh_int2scm (multiplicity));
769 Real thick = gh_scm2double (me->get_grob_property ("thickness")) ;
770 Real interbeam_f = gh_scm2double (space) ;
772 Real bdy = interbeam_f;
773 Real stemdx = staffline_f;
775 // ugh -> use commonx
776 Real dx = visible_stem_count (me) ?
777 last_visible_stem (me)->relative_coordinate (0, X_AXIS) - first_visible_stem (me)->relative_coordinate (0, X_AXIS)
778 : 0.0;
780 Molecule leftbeams;
781 Molecule rightbeams;
783 Real nw_f;
784 if (!Stem::first_head (here))
785 nw_f = 0;
786 else {
787 int t = Stem::type_i (here);
789 SCM proc = me->get_grob_property ("flag-width-function");
790 SCM result = gh_call1 (proc, gh_int2scm (t));
791 nw_f = gh_scm2double (result);
795 Direction dir = Directional_element_interface::get (me);
797 /* half beams extending to the left. */
798 if (prev)
800 int lhalfs= lhalfs = Stem::beam_count (here,LEFT) - Stem::beam_count (prev,RIGHT);
801 int lwholebeams= Stem::beam_count (here,LEFT) <? Stem::beam_count (prev,RIGHT) ;
803 Half beam should be one note-width,
804 but let's make sure two half-beams never touch
806 Real w = here->relative_coordinate (0, X_AXIS) - prev->relative_coordinate (0, X_AXIS);
807 w = w/2 <? nw_f;
808 Molecule a;
809 if (lhalfs) // generates warnings if not
810 a = Lookup::beam (dydx, w, thick);
811 a.translate (Offset (-w, -w * dydx));
812 for (int j = 0; j < lhalfs; j++)
814 Molecule b (a);
815 b.translate_axis (-dir * bdy * (lwholebeams+j), Y_AXIS);
816 leftbeams.add_molecule (b);
820 if (next)
822 int rhalfs = Stem::beam_count (here,RIGHT) - Stem::beam_count (next,LEFT);
823 int rwholebeams= Stem::beam_count (here,RIGHT) <? Stem::beam_count (next,LEFT) ;
825 Real w = next->relative_coordinate (0, X_AXIS) - here->relative_coordinate (0, X_AXIS);
826 Molecule a = Lookup::beam (dydx, w + stemdx, thick);
827 a.translate_axis (- stemdx/2, X_AXIS);
828 int j = 0;
829 Real gap_f = 0;
831 SCM gap = me->get_grob_property ("gap");
832 if (gh_number_p (gap))
834 int gap_i = gh_scm2int ((gap));
835 int nogap = rwholebeams - gap_i;
837 for (; j < nogap; j++)
839 Molecule b (a);
840 b.translate_axis (-dir * bdy * j, Y_AXIS);
841 rightbeams.add_molecule (b);
843 // TODO: notehead widths differ for different types
844 gap_f = nw_f / 2;
845 w -= 2 * gap_f;
846 a = Lookup::beam (dydx, w + stemdx, thick);
849 for (; j < rwholebeams; j++)
851 Molecule b (a);
852 b.translate (Offset (Stem::invisible_b (here) ? 0 : gap_f, -dir * bdy * j));
853 rightbeams.add_molecule (b);
856 w = w/2 <? nw_f;
857 if (rhalfs)
858 a = Lookup::beam (dydx, w, thick);
860 for (; j < rwholebeams + rhalfs; j++)
862 Molecule b (a);
863 b.translate_axis (- dir * bdy * j, Y_AXIS);
864 rightbeams.add_molecule (b);
868 leftbeams.add_molecule (rightbeams);
871 Does beam quanting think of the asymetry of beams?
872 Refpoint is on bottom of symbol. (FIXTHAT) --hwn.
874 return leftbeams;
877 MAKE_SCHEME_CALLBACK (Beam,brew_molecule,1);
879 Beam::brew_molecule (SCM smob)
881 Grob * me =unsmob_grob (smob);
883 Molecule mol;
884 if (!gh_pair_p (me->get_grob_property ("stems")))
885 return SCM_EOL;
886 Real x0,dx;
887 Link_array<Item>stems =
888 Pointer_group_interface__extract_elements (me, (Item*) 0, "stems");
889 if (visible_stem_count (me))
891 // ugh -> use commonx
892 x0 = first_visible_stem (me)->relative_coordinate (0, X_AXIS);
893 dx = last_visible_stem (me)->relative_coordinate (0, X_AXIS) - x0;
895 else
897 x0 = stems[0]->relative_coordinate (0, X_AXIS);
898 dx = stems.top ()->relative_coordinate (0, X_AXIS) - x0;
904 TODO: the naming of the grob properties sucks.
906 SCM dy_s = me->get_grob_property ("dy");
907 SCM y_s = me->get_grob_property ("y");
910 Real dy = gh_number_p (dy_s) ? gh_scm2double (dy_s) : 0.0;
911 Real dydx = dy && dx ? dy/dx : 0;
912 Real y = gh_number_p (y_s) ? gh_scm2double (y_s) : 0.0;
915 for (int j=0; j <stems.size (); j++)
917 Item *i = stems[j];
918 Item * prev = (j > 0)? stems[j-1] : 0;
919 Item * next = (j < stems.size ()-1) ? stems[j+1] :0;
921 Molecule sb = stem_beams (me, i, next, prev, dy, dydx);
922 Real x = i->relative_coordinate (0, X_AXIS)-x0;
923 sb.translate (Offset (x, x * dydx + y));
924 mol.add_molecule (sb);
926 mol.translate_axis (x0
927 - dynamic_cast<Spanner*> (me)->get_bound (LEFT)->relative_coordinate (0, X_AXIS), X_AXIS);
929 return mol.smobbed_copy ();
933 Beam::forced_stem_count (Grob*me)
935 Link_array<Item>stems =
936 Pointer_group_interface__extract_elements (me, (Item*) 0, "stems");
937 int f = 0;
938 for (int i=0; i < stems.size (); i++)
940 Item *s = stems[i];
942 if (Stem::invisible_b (s))
943 continue;
945 if (( (int)Stem::chord_start_f (s))
946 && (Stem::get_direction (s) != Stem::get_default_dir (s)))
947 f++;
949 return f;
955 /* TODO:
956 use filter and standard list functions.
959 Beam::visible_stem_count (Grob*me)
961 Link_array<Item>stems =
962 Pointer_group_interface__extract_elements (me, (Item*) 0, "stems");
963 int c = 0;
964 for (int i = stems.size (); i--;)
966 if (!Stem::invisible_b (stems[i]))
967 c++;
969 return c;
972 Item*
973 Beam::first_visible_stem (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++)
980 if (!Stem::invisible_b (stems[i]))
981 return stems[i];
983 return 0;
986 Item*
987 Beam::last_visible_stem (Grob*me)
989 Link_array<Item>stems =
990 Pointer_group_interface__extract_elements (me, (Item*) 0, "stems");
991 for (int i = stems.size (); i--;)
993 if (!Stem::invisible_b (stems[i]))
994 return stems[i];
996 return 0;
1001 [TODO]
1002 handle rest under beam (do_post: beams are calculated now)
1003 what about combination of collisions and rest under beam.
1005 Should lookup
1007 rest -> stem -> beam -> interpolate_y_position ()
1009 MAKE_SCHEME_CALLBACK (Beam,rest_collision_callback,2);
1011 Beam::rest_collision_callback (SCM element_smob, SCM axis)
1013 Grob *rest = unsmob_grob (element_smob);
1014 Axis a = (Axis) gh_scm2int (axis);
1016 assert (a == Y_AXIS);
1018 Grob * st = unsmob_grob (rest->get_grob_property ("stem"));
1019 Grob * stem = st;
1020 if (!stem)
1021 return gh_double2scm (0.0);
1022 Grob * beam = unsmob_grob (stem->get_grob_property ("beam"));
1023 if (!beam || !Beam::has_interface (beam) || !Beam::visible_stem_count (beam))
1024 return gh_double2scm (0.0);
1026 // make callback for rest from this.
1027 Real beam_dy = 0;
1028 Real beam_y = 0;
1031 // todo: make sure this calced already.
1032 SCM s = beam->get_grob_property ("dy");
1033 if (gh_number_p (s))
1034 beam_dy = gh_scm2double (s);
1036 s = beam->get_grob_property ("y");
1037 if (gh_number_p (s))
1038 beam_y = gh_scm2double (s);
1040 // ugh -> use commonx
1041 Real x0 = first_visible_stem (beam)->relative_coordinate (0, X_AXIS);
1042 Real dx = last_visible_stem (beam)->relative_coordinate (0, X_AXIS) - x0;
1043 Real dydx = beam_dy && dx ? beam_dy/dx : 0;
1045 Direction d = Stem::get_direction (stem);
1046 Real beamy = (stem->relative_coordinate (0, X_AXIS) - x0) * dydx + beam_y;
1048 Real staff_space = Staff_symbol_referencer::staff_space (rest);
1051 Real rest_dim = rest->extent (rest, Y_AXIS)[d]*2.0 / staff_space ; // refp??
1053 Real minimum_dist
1054 = gh_scm2double (rest->get_grob_property ("minimum-beam-collision-distance"));
1055 Real dist =
1056 minimum_dist + -d * (beamy - rest_dim) >? 0;
1058 int stafflines = Staff_symbol_referencer::line_count (rest);
1060 // move discretely by half spaces.
1061 int discrete_dist = int (ceil (dist));
1063 // move by whole spaces inside the staff.
1064 if (discrete_dist < stafflines+1)
1065 discrete_dist = int (ceil (discrete_dist / 2.0)* 2.0);
1067 return gh_double2scm (-d * discrete_dist);
1071 bool
1072 Beam::has_interface (Grob*me)
1074 return me->has_interface (ly_symbol2scm ("beam-interface"));