2 beam.cc -- implement Beam
4 source file of the GNU LilyPond music typesetter
6 (c) 1997--1999 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7 Jan Nieuwenhuizen <janneke@gnu.org>
14 * move paper vars to scm
17 #include <math.h> // tanh.
19 #include "directional-element-interface.hh"
21 #include "dimensions.hh"
25 #include "leastsquares.hh"
27 #include "paper-def.hh"
29 #include "group-interface.hh"
30 #include "staff-symbol-referencer.hh"
31 #include "cross-staff.hh"
32 #include "lily-guile.icc"
36 Group_interface
g (this, "stems");
41 Beam::add_stem (Stem
*s
)
43 Group_interface
gi (this, "stems");
46 s
->add_dependency (this);
48 assert (!s
->beam_l ());
49 s
->set_elt_property ("beam", self_scm_
);
51 if (!spanned_drul_
[LEFT
])
58 Beam::get_multiplicity () const
61 for (SCM s
= get_elt_property ("stems"); gh_pair_p (s
); s
= gh_cdr (s
))
63 Score_element
* sc
= unsmob_element (gh_car (s
));
65 if (Stem
* st
= dynamic_cast<Stem
*> (sc
))
66 m
= m
>? st
->beam_count (LEFT
) >? st
->beam_count (RIGHT
);
72 After pre-processing all directions should be set.
73 Several post-processing routines (stem, slur, script) need stem/beam
75 Currenly, this means that beam has set all stem's directions.
76 [Alternatively, stems could set its own directions, according to
77 their beam, during 'final-pre-processing'.]
80 Beam::do_pre_processing ()
83 if (visible_stem_count () < 2)
85 warning (_ ("beam has less than two stems"));
86 set_elt_property ("transparent", SCM_BOOL_T
);
89 if (!directional_element (this).get ())
90 directional_element (this).set (get_default_dir ());
93 set_stem_directions ();
102 Beam::get_default_dir () const
104 Drul_array
<int> total
;
105 total
[UP
] = total
[DOWN
] = 0;
106 Drul_array
<int> count
;
107 count
[UP
] = count
[DOWN
] = 0;
110 for (int i
=0; i
<stem_count (); i
++)
111 do { // HUH -- waar slaat dit op?
113 Direction sd
= directional_element (s
).get ();
114 int current
= sd
? (1 + d
* sd
)/2
115 : s
->get_center_distance ((Direction
)-d
);
123 } while (flip(&d
) != DOWN
);
126 SCM s
= scm_eval (gh_list (ly_symbol2scm ("beam-dir-algorithm"),
127 ly_quote_scm (gh_cons (gh_int2scm (count
[UP
]),
128 gh_int2scm (count
[DOWN
]))),
129 ly_quote_scm (gh_cons (gh_int2scm (total
[UP
]),
130 gh_int2scm (total
[DOWN
]))),
132 if (gh_number_p (s
) && gh_scm2int (s
))
136 If dir is not determined: get from paper
138 return (Direction
)(int)
139 paper_l ()->get_var ("stem_default_neutral_direction");
144 Set all stems with non-forced direction to beam direction.
145 Urg: non-forced should become `without/with unforced' direction,
146 once stem gets cleaned-up.
149 Beam::set_stem_directions ()
151 Direction d
= directional_element (this).get ();
152 for (int i
=0; i
<stem_count (); i
++)
155 SCM force
= s
->remove_elt_property ("dir-forced");
156 if (!gh_boolean_p (force
) || !gh_scm2bool (force
))
157 directional_element (s
).set (d
);
164 if (!auto_knee ("auto-interstaff-knee-gap", true))
165 auto_knee ("auto-knee-gap", false);
169 Simplistic auto-knees; only consider vertical gap between two
172 `Forced' stem directions are ignored. If you don't want auto-knees,
173 don't set, or unset autoKneeGap/autoInterstaffKneeGap.
176 Beam::auto_knee (String gap_str
, bool interstaff_b
)
180 SCM gap
= get_elt_property (gap_str
);
181 Direction d
= directional_element (this).get ();
183 if (gh_number_p (gap
))
185 int auto_gap_i
= gh_scm2int (gap
);
186 for (int i
=1; i
< stem_count (); i
++)
188 bool is_b
= (bool)(calc_interstaff_dist (stem (i
), this)
189 - calc_interstaff_dist (stem (i
-1), this));
190 int l_y
= (int)(stem (i
-1)->head_positions()[d
])
191 + (int)calc_interstaff_dist (stem (i
-1), this);
192 int r_y
= (int)(stem (i
)->head_positions()[d
])
193 + (int)calc_interstaff_dist (stem (i
), this);
194 int gap_i
= r_y
- l_y
;
196 if ((abs (gap_i
) >= auto_gap_i
) && (!interstaff_b
|| is_b
))
198 knee_y
= (r_y
+ l_y
) / 2;
206 for (int i
=0; i
< stem_count (); i
++)
208 int y
= (int)(stem (i
)->head_positions()[d
])
209 + (int)calc_interstaff_dist (stem (i
), this);
210 directional_element (stem (i
)).set (y
< knee_y
? UP
: DOWN
);
211 stem (i
)->set_elt_property ("dir-forced", SCM_BOOL_T
);
218 Set stem's shorten property if unset.
220 take some y-position (chord/beam/nearest?) into account
221 scmify forced-fraction
224 Beam::set_stem_shorten ()
226 if (!visible_stem_count ())
229 Real forced_fraction
= forced_stem_count () / visible_stem_count ();
230 if (forced_fraction
< 0.5)
233 int multiplicity
= get_multiplicity ();
235 SCM shorten
= ly_eval_str ("beamed-stem-shorten");
238 scm_to_array (shorten
, &a
);
242 Staff_symbol_referencer_interface
st (this);
243 Real staff_space
= st
.staff_space ();
244 Real shorten_f
= a
[multiplicity
<? (a
.size () - 1)] * staff_space
;
246 /* cute, but who invented this -- how to customise ? */
247 if (forced_fraction
< 1)
250 for (int i
=0; i
< stem_count (); i
++)
253 if (s
->invisible_b ())
255 if (gh_number_p (s
->get_elt_property ("shorten")))
256 s
->set_elt_property ("shorten", gh_double2scm (shorten_f
));
261 Set elt properties height and y-position if not set.
262 Adjust stem lengths to reach beam.
265 Beam::do_post_processing ()
267 /* first, calculate y, dy */
269 calc_position_and_height (&y
, &dy
);
270 if (suspect_slope_b (y
, dy
))
273 Real damped_dy
= calc_slope_damping_f (dy
);
274 Real quantised_dy
= quantise_dy_f (damped_dy
);
276 y
+= (dy
- quantised_dy
) / 2;
280 until here, we used only stem_info, which acts as if dir=up
282 y
*= directional_element (this).get ();
283 dy
*= directional_element (this).get ();
285 /* set or read dy as necessary */
286 SCM s
= get_elt_property ("height");
288 dy
= gh_scm2double (s
);
290 set_elt_property ("height", gh_double2scm (dy
));
292 /* set or read y as necessary */
293 s
= get_elt_property ("y-position");
296 y
= gh_scm2double (s
);
297 set_stem_length (y
, dy
);
301 /* we can modify y, so we should quantise y */
302 Real y_shift
= check_stem_length_f (y
, dy
);
304 y
= quantise_y_f (y
, dy
, 0);
305 set_stem_length (y
, dy
);
306 y_shift
= check_stem_length_f (y
, dy
);
308 Staff_symbol_referencer_interface
st (this);
309 Real half_space
= st
.staff_space () / 2;
310 if (y_shift
> half_space
/ 4)
315 for significantly lengthened or shortened stems,
316 request quanting the other way.
319 if (abs (y_shift
) > half_space
/ 2)
320 quant_dir
= sign (y_shift
) * directional_element (this).get ();
321 y
= quantise_y_f (y
, dy
, quant_dir
);
322 set_stem_length (y
, dy
);
325 set_elt_property ("y-position", gh_double2scm (y
));
330 See Documentation/tex/fonts.doc
333 Beam::calc_position_and_height (Real
* y
, Real
* dy
) const
336 if (visible_stem_count () <= 1)
339 Real first_ideal
= first_visible_stem ()->calc_stem_info ().idealy_f_
;
340 if (first_ideal
== last_visible_stem ()->calc_stem_info ().idealy_f_
)
348 Real x0
= first_visible_stem ()->hpos_f ();
349 for (int i
=0; i
< stem_count (); i
++)
352 if (s
->invisible_b ())
354 ls
.input
.push (Offset (s
->hpos_f () - x0
,
355 s
->calc_stem_info ().idealy_f_
));
358 ls
.minimise (dydx
, *y
); // duh, takes references
360 Real dx
= last_visible_stem ()->hpos_f () - x0
;
365 Beam::suspect_slope_b (Real y
, Real dy
) const
368 steep slope running against lengthened stem is suspect
370 Real first_ideal
= first_visible_stem ()->calc_stem_info ().idealy_f_
;
371 Real last_ideal
= last_visible_stem ()->calc_stem_info ().idealy_f_
;
372 Real lengthened
= paper_l ()->get_var ("beam_lengthened");
373 Real steep
= paper_l ()->get_var ("beam_steep_slope");
375 Real dx
= last_visible_stem ()->hpos_f () - first_visible_stem ()->hpos_f ();
376 Real dydx
= dy
&& dx
? dy
/dx
: 0;
378 if (((y
- first_ideal
> lengthened
) && (dydx
> steep
))
379 || ((y
+ dy
- last_ideal
> lengthened
) && (dydx
< -steep
)))
387 This neat trick is by Werner Lemberg,
388 damped = tanh (slope)
389 corresponds with some tables in [Wanske]
392 Beam::calc_slope_damping_f (Real dy
) const
394 SCM damp
= get_elt_property ("damping"); // remove?
395 int damping
= 1; // ugh.
396 if (gh_number_p (damp
))
397 damping
= gh_scm2int (damp
);
401 Real dx
= last_visible_stem ()->hpos_f ()
402 - first_visible_stem ()->hpos_f ();
403 Real dydx
= dy
&& dx
? dy
/dx
: 0;
404 dydx
= 0.6 * tanh (dydx
) / damping
;
411 Beam::calc_stem_y_f (Stem
* s
, Real y
, Real dy
) const
413 Real thick
= gh_scm2double (get_elt_property ("beam-thickness"));
414 int beam_multiplicity
= get_multiplicity ();
415 int stem_multiplicity
= (s
->flag_i () - 2) >? 0;
417 Real interbeam_f
= paper_l ()->interbeam_f (beam_multiplicity
);
418 Real x0
= first_visible_stem ()->hpos_f ();
419 Real dx
= last_visible_stem ()->hpos_f () - x0
;
420 Real stem_y
= (dy
&& dx
? (s
->hpos_f () - x0
) / dx
* dy
: 0) + y
;
423 Direction dir
= directional_element(this).get ();
424 Direction sdir
= directional_element (s
).get ();
430 * (thick
/ 2 + (beam_multiplicity
- 1) * interbeam_f
);
432 Staff_symbol_referencer_interface
me (s
);
433 Staff_symbol_referencer_interface
last (last_visible_stem ());
435 // huh, why not for first visible?
436 if (//(s != first_visible_stem ()) &&
437 me
.staff_symbol_l () != last
.staff_symbol_l ())
438 stem_y
+= directional_element (this).get ()
439 * (beam_multiplicity
- stem_multiplicity
) * interbeam_f
;
445 Beam::check_stem_length_f (Real y
, Real dy
) const
449 Direction dir
= directional_element (this).get ();
451 for (int i
=0; i
< stem_count (); i
++)
454 if (s
->invisible_b ())
457 Real stem_y
= calc_stem_y_f (s
, y
, dy
);
460 Stem_info info
= s
->calc_stem_info ();
462 // if (0 > info.maxy_f_ - stem_y)
463 shorten
= shorten
<? info
.maxy_f_
- stem_y
;
464 // if (0 < info.miny_f_ - stem_y)
465 lengthen
= lengthen
>? info
.miny_f_
- stem_y
;
468 if (lengthen
&& shorten
)
469 warning (_ ("weird beam vertical offset"));
471 /* when all stems are too short, normal stems win */
472 return dir
* ((shorten
) ? shorten
: lengthen
);
476 Hmm. At this time, beam position and slope are determined. Maybe,
477 stem directions and length should set to relative to the chord's
478 position of the beam. */
480 Beam::set_stem_length (Real y
, Real dy
)
482 Staff_symbol_referencer_interface
st (this);
483 Real half_space
= st
.staff_space ()/2;
484 for (int i
=0; i
< stem_count (); i
++)
487 if (s
->invisible_b ())
490 Real stem_y
= calc_stem_y_f (s
, y
, dy
);
492 /* caution: stem measures in staff-positions */
493 s
->set_stemend ((stem_y
+ calc_interstaff_dist (s
, this)) / half_space
);
498 [Ross] (simplification of)
499 Set dy complying with:
501 - thick / 2 + staffline_f / 2
502 - thick + staffline_f
506 Beam::quantise_dy_f (Real dy
) const
508 SCM quants
= ly_eval_str ("beam-height-quants");
511 scm_to_array (quants
, &a
);
515 Staff_symbol_referencer_interface
st (this);
516 Real staff_space
= st
.staff_space ();
518 Interval iv
= quantise_iv (a
, abs (dy
)/staff_space
) * staff_space
;
519 Real q
= (abs (dy
) - iv
[SMALLER
] <= iv
[BIGGER
] - abs (dy
))
523 return q
* sign (dy
);
527 Prevent interference from stafflines and beams.
528 See Documentation/tex/fonts.doc
530 We only need to quantise the (left) y-position of the beam,
531 since dy is quantised too.
532 if extend_b then stems must *not* get shorter
535 Beam::quantise_y_f (Real y
, Real dy
, int quant_dir
)
537 int multiplicity
= get_multiplicity ();
538 Staff_symbol_referencer_interface
st (this);
539 Real staff_space
= st
.staff_space ();
540 SCM quants
= scm_eval (gh_list (
541 ly_symbol2scm ("beam-vertical-position-quants"),
542 gh_int2scm (multiplicity
),
543 gh_double2scm (dy
/staff_space
),
546 scm_to_array (quants
, &a
);
550 Real up_y
= directional_element (this).get () * y
;
551 Interval iv
= quantise_iv (a
, up_y
/staff_space
) * staff_space
;
553 Real q
= up_y
- iv
[SMALLER
] <= iv
[BIGGER
] - up_y
554 ? iv
[SMALLER
] : iv
[BIGGER
];
556 q
= iv
[(Direction
)quant_dir
];
558 return q
* directional_element (this).get ();
562 Beam::set_beaming (Beaming_info_list
*beaming
)
565 for (int i
=0; i
< stem_count (); i
++)
569 if (stem (i
)->beam_count (d
) == 0)
570 stem (i
)->set_beaming ( beaming
->infos_
.elem (i
).beams_i_drul_
[d
],d
);
572 while (flip (&d
) != LEFT
);
579 beams to go with one stem.
585 Beam::stem_beams (Stem
*here
, Stem
*next
, Stem
*prev
) const
587 if ((next
&& !(next
->hpos_f () > here
->hpos_f ())) ||
588 (prev
&& !(prev
->hpos_f () < here
->hpos_f ())))
589 programming_error ("Beams are not left-to-right");
591 Real staffline_f
= paper_l ()->get_var ("stafflinethickness");
592 int multiplicity
= get_multiplicity ();
595 Real interbeam_f
= paper_l ()->interbeam_f (multiplicity
);
596 Real thick
= gh_scm2double (get_elt_property ("beam-thickness"));;
598 Real bdy
= interbeam_f
;
599 Real stemdx
= staffline_f
;
601 Real dx
= last_visible_stem ()->hpos_f () - first_visible_stem ()->hpos_f ();
602 Real dy
= get_real ("height");
603 Real dydx
= dy
&& dx
? dy
/dx
: 0;
610 if (!here
->first_head ())
612 else if (here
->type_i ()== 1)
613 nw_f
= paper_l ()->get_var ("wholewidth");
614 else if (here
->type_i () == 2)
615 nw_f
= paper_l ()->get_var ("notewidth") * 0.8;
617 nw_f
= paper_l ()->get_var ("quartwidth");
620 Direction dir
= directional_element (this).get ();
622 /* half beams extending to the left. */
625 int lhalfs
= lhalfs
= here
->beam_count (LEFT
) - prev
->beam_count (RIGHT
);
626 int lwholebeams
= here
->beam_count (LEFT
) <? prev
->beam_count (RIGHT
) ;
628 Half beam should be one note-width,
629 but let's make sure two half-beams never touch
631 Real w
= here
->hpos_f () - prev
->hpos_f ();
634 if (lhalfs
) // generates warnings if not
635 a
= lookup_l ()->beam (dydx
, w
, thick
);
636 a
.translate (Offset (-w
, -w
* dydx
));
637 for (int j
= 0; j
< lhalfs
; j
++)
640 b
.translate_axis (-dir
* bdy
* (lwholebeams
+j
), Y_AXIS
);
641 leftbeams
.add_molecule (b
);
647 int rhalfs
= here
->beam_count (RIGHT
) - next
->beam_count (LEFT
);
648 int rwholebeams
= here
->beam_count (RIGHT
) <? next
->beam_count (LEFT
) ;
650 Real w
= next
->hpos_f () - here
->hpos_f ();
651 Molecule a
= lookup_l ()->beam (dydx
, w
+ stemdx
, thick
);
652 a
.translate_axis( - stemdx
/2, X_AXIS
);
656 SCM gap
= get_elt_property ("beam-gap");
657 if (gh_number_p (gap
))
659 int gap_i
= gh_scm2int ( (gap
));
660 int nogap
= rwholebeams
- gap_i
;
662 for (; j
< nogap
; j
++)
665 b
.translate_axis (-dir
* bdy
* j
, Y_AXIS
);
666 rightbeams
.add_molecule (b
);
668 // TODO: notehead widths differ for different types
671 a
= lookup_l ()->beam (dydx
, w
+ stemdx
, thick
);
674 for (; j
< rwholebeams
; j
++)
677 b
.translate (Offset (here
->invisible_b () ? 0 : gap_f
, -dir
* bdy
* j
));
678 rightbeams
.add_molecule (b
);
683 a
= lookup_l ()->beam (dydx
, w
, thick
);
685 for (; j
< rwholebeams
+ rhalfs
; j
++)
688 b
.translate_axis (- dir
* bdy
* j
, Y_AXIS
);
689 rightbeams
.add_molecule (b
);
693 leftbeams
.add_molecule (rightbeams
);
696 Does beam quanting think of the asymetry of beams?
697 Refpoint is on bottom of symbol. (FIXTHAT) --hwn.
704 Beam::do_brew_molecule_p () const
706 Molecule
*mol_p
= new Molecule
;
710 Real x0
= first_visible_stem ()->hpos_f ();
711 Real dx
= last_visible_stem ()->hpos_f () - x0
;
712 Real dy
= get_real ("height");
713 Real dydx
= dy
&& dx
? dy
/dx
: 0;
714 Real y
= get_real ("y-position");
715 for (int j
=0; j
<stem_count (); j
++)
718 Stem
* prev
= (j
> 0)? stem (j
-1) : 0;
719 Stem
* next
= (j
< stem_count ()-1) ? stem (j
+1) :0;
721 Molecule sb
= stem_beams (i
, next
, prev
);
722 Real x
= i
->hpos_f ()-x0
;
723 sb
.translate (Offset (x
, x
* dydx
+ y
));
724 mol_p
->add_molecule (sb
);
726 mol_p
->translate_axis (x0
727 - spanned_drul_
[LEFT
]->relative_coordinate (0, X_AXIS
), X_AXIS
);
733 Beam::forced_stem_count () const
736 for (int i
=0; i
< stem_count (); i
++)
740 if (s
->invisible_b ())
743 if (((int)s
->chord_start_f ())
744 && (s
->get_direction () != s
->get_default_dir ()))
753 TODO: Fix this class. This is wildly inefficient.
754 And it sux. Yet another array/list 'interface'.
757 Beam::stem (int i
) const
759 return Group_interface__extract_elements ((Beam
*) this, (Stem
*) 0, "stems")[i
];
763 Beam::stem_count () const
765 Group_interface
gi (this, "stems");
770 Beam::stem_top () const
772 SCM s
= get_elt_property ("stems");
774 return gh_pair_p (s
) ? dynamic_cast<Stem
*> (unsmob_element (gh_car (s
))) : 0;
776 //Group_interface__extract_elements ((Beam*) this, (Stem*) 0, "stems")[stem_count () - 1];
781 Beam::visible_stem_count () const
784 for (int i
= 0; i
< stem_count (); i
++)
786 if (!stem (i
)->invisible_b ())
793 Beam::first_visible_stem () const
795 for (int i
= 0; i
< stem_count (); i
++)
798 if (!s
->invisible_b ())
808 Beam::last_visible_stem () const
810 for (int i
= stem_count (); i
> 0; i
--)
812 Stem
* s
= stem (i
- 1);
813 if (!s
->invisible_b ())