2 beam.cc -- implement Beam
4 source file of the GNU LilyPond music typesetter
6 (c) 1997--2000 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7 Jan Nieuwenhuizen <janneke@gnu.org>
14 * move paper vars to scm
19 #include <math.h> // tanh.
21 #include "directional-element-interface.hh"
23 #include "dimensions.hh"
27 #include "least-squares.hh"
29 #include "paper-def.hh"
31 #include "group-interface.hh"
32 #include "staff-symbol-referencer.hh"
33 #include "cross-staff.hh"
34 #include "lily-guile.icc"
38 Group_interface
g (this, "stems");
43 Beam::add_stem (Stem
*s
)
45 Group_interface
gi (this, "stems");
48 s
->add_dependency (this);
50 assert (!s
->beam_l ());
51 s
->set_elt_property ("beam", self_scm_
);
53 if (!spanned_drul_
[LEFT
])
60 Beam::get_multiplicity () const
63 for (SCM s
= get_elt_property ("stems"); gh_pair_p (s
); s
= gh_cdr (s
))
65 Score_element
* sc
= unsmob_element (gh_car (s
));
67 if (Stem
* st
= dynamic_cast<Stem
*> (sc
))
68 m
= m
>? st
->beam_count (LEFT
) >? st
->beam_count (RIGHT
);
74 After pre-processing all directions should be set.
75 Several post-processing routines (stem, slur, script) need stem/beam
77 Currenly, this means that beam has set all stem's directions.
78 [Alternatively, stems could set its own directions, according to
79 their beam, during 'final-pre-processing'.]
82 Beam::do_pre_processing ()
85 if (visible_stem_count () < 2)
87 warning (_ ("beam has less than two stems"));
88 // set_elt_property ("transparent", SCM_BOOL_T);
91 if (!directional_element (this).get ())
92 directional_element (this).set (get_default_dir ());
95 set_stem_directions ();
103 Beam::get_default_dir () const
105 Drul_array
<int> total
;
106 total
[UP
] = total
[DOWN
] = 0;
107 Drul_array
<int> count
;
108 count
[UP
] = count
[DOWN
] = 0;
111 for (int i
=0; i
<stem_count (); i
++)
112 do { // HUH -- waar slaat dit op?
114 Direction sd
= directional_element (s
).get ();
115 int current
= sd
? (1 + d
* sd
)/2
116 : s
->get_center_distance ((Direction
)-d
);
124 } while (flip(&d
) != DOWN
);
127 SCM s
= scm_eval (gh_list (ly_symbol2scm ("beam-dir-algorithm"),
128 ly_quote_scm (gh_cons (gh_int2scm (count
[UP
]),
129 gh_int2scm (count
[DOWN
]))),
130 ly_quote_scm (gh_cons (gh_int2scm (total
[UP
]),
131 gh_int2scm (total
[DOWN
]))),
133 if (gh_number_p (s
) && gh_scm2int (s
))
137 If dir is not determined: get from paper
139 return (Direction
)(int)
140 paper_l ()->get_var ("stem_default_neutral_direction");
145 Set all stems with non-forced direction to beam direction.
146 Urg: non-forced should become `without/with unforced' direction,
147 once stem gets cleaned-up.
150 Beam::set_stem_directions ()
152 Direction d
= directional_element (this).get ();
153 for (int i
=0; i
<stem_count (); i
++)
156 SCM force
= s
->remove_elt_property ("dir-forced");
157 if (!gh_boolean_p (force
) || !gh_scm2bool (force
))
158 directional_element (s
).set (d
);
165 if (!auto_knee ("auto-interstaff-knee-gap", true))
166 auto_knee ("auto-knee-gap", false);
170 Simplistic auto-knees; only consider vertical gap between two
173 `Forced' stem directions are ignored. If you don't want auto-knees,
174 don't set, or unset autoKneeGap/autoInterstaffKneeGap.
177 Beam::auto_knee (String gap_str
, bool interstaff_b
)
181 SCM gap
= get_elt_property (gap_str
);
182 Direction d
= directional_element (this).get ();
184 if (gh_number_p (gap
))
186 int auto_gap_i
= gh_scm2int (gap
);
187 for (int i
=1; i
< stem_count (); i
++)
189 bool is_b
= (bool)(calc_interstaff_dist (stem (i
), this)
190 - calc_interstaff_dist (stem (i
-1), this));
191 int l_y
= (int)(stem (i
-1)->head_positions()[d
])
192 + (int)calc_interstaff_dist (stem (i
-1), this);
193 int r_y
= (int)(stem (i
)->head_positions()[d
])
194 + (int)calc_interstaff_dist (stem (i
), this);
195 int gap_i
= r_y
- l_y
;
197 if ((abs (gap_i
) >= auto_gap_i
) && (!interstaff_b
|| is_b
))
199 knee_y
= (r_y
+ l_y
) / 2;
207 for (int i
=0; i
< stem_count (); i
++)
209 int y
= (int)(stem (i
)->head_positions()[d
])
210 + (int)calc_interstaff_dist (stem (i
), this);
211 directional_element (stem (i
)).set (y
< knee_y
? UP
: DOWN
);
212 stem (i
)->set_elt_property ("dir-forced", SCM_BOOL_T
);
219 Set stem's shorten property if unset.
221 take some y-position (chord/beam/nearest?) into account
222 scmify forced-fraction
225 Beam::set_stem_shorten ()
227 if (!visible_stem_count ())
230 Real forced_fraction
= forced_stem_count () / visible_stem_count ();
231 if (forced_fraction
< 0.5)
234 int multiplicity
= get_multiplicity ();
236 SCM shorten
= ly_eval_str ("beamed-stem-shorten");
239 scm_to_array (shorten
, &a
);
243 Staff_symbol_referencer_interface
st (this);
244 Real staff_space
= st
.staff_space ();
245 Real shorten_f
= a
[multiplicity
<? (a
.size () - 1)] * staff_space
;
247 /* cute, but who invented this -- how to customise ? */
248 if (forced_fraction
< 1)
251 for (int i
=0; i
< stem_count (); i
++)
254 if (s
->invisible_b ())
256 if (gh_number_p (s
->get_elt_property ("shorten")))
257 s
->set_elt_property ("shorten", gh_double2scm (shorten_f
));
262 Set elt properties height and y-position if not set.
263 Adjust stem lengths to reach beam.
266 Beam::do_post_processing ()
268 /* first, calculate y, dy */
270 calc_position_and_height (&y
, &dy
);
271 if (visible_stem_count ())
273 if (suspect_slope_b (y
, dy
))
276 Real damped_dy
= calc_slope_damping_f (dy
);
277 Real quantised_dy
= quantise_dy_f (damped_dy
);
279 y
+= (dy
- quantised_dy
) / 2;
283 until here, we used only stem_info, which acts as if dir=up
285 y
*= directional_element (this).get ();
286 dy
*= directional_element (this).get ();
288 Staff_symbol_referencer_interface
st (this);
289 Real half_space
= st
.staff_space () / 2;
291 /* check for user-override of dy */
292 SCM s
= remove_elt_property ("height-hs");
295 dy
= gh_scm2double (s
) * half_space
;
297 set_elt_property ("height", gh_double2scm (dy
));
299 /* check for user-override of y */
300 s
= remove_elt_property ("y-position-hs");
303 y
= gh_scm2double (s
) * half_space
;
307 /* we can modify y, so we should quantise y */
308 Real y_shift
= check_stem_length_f (y
, dy
);
310 y
= quantise_y_f (y
, dy
, 0);
311 set_stem_length (y
, dy
);
312 y_shift
= check_stem_length_f (y
, dy
);
314 if (y_shift
> half_space
/ 4)
319 for significantly lengthened or shortened stems,
320 request quanting the other way.
323 if (abs (y_shift
) > half_space
/ 2)
324 quant_dir
= sign (y_shift
) * directional_element (this).get ();
325 y
= quantise_y_f (y
, dy
, quant_dir
);
328 // UGH. Y is not in staff position unit?
329 // Ik dacht datwe daar juist van weg wilden?
330 set_stem_length (y
, dy
);
331 set_elt_property ("y-position", gh_double2scm (y
));
335 See Documentation/tex/fonts.doc
338 Beam::calc_position_and_height (Real
* y
, Real
* dy
) const
341 if (visible_stem_count () <= 1)
344 Real first_ideal
= first_visible_stem ()->calc_stem_info ().idealy_f_
;
345 if (first_ideal
== last_visible_stem ()->calc_stem_info ().idealy_f_
)
352 Array
<Offset
> ideals
;
353 Real x0
= first_visible_stem ()->hpos_f ();
354 for (int i
=0; i
< stem_count (); i
++)
357 if (s
->invisible_b ())
359 ideals
.push (Offset (s
->hpos_f () - x0
,
360 s
->calc_stem_info ().idealy_f_
));
363 minimise_least_squares (&dydx
, y
, ideals
); // duh, takes references
365 Real dx
= last_visible_stem ()->hpos_f () - x0
;
370 Beam::suspect_slope_b (Real y
, Real dy
) const
372 /* first, calculate y, dy */
374 steep slope running against lengthened stem is suspect
376 Real first_ideal
= first_visible_stem ()->calc_stem_info ().idealy_f_
;
377 Real last_ideal
= last_visible_stem ()->calc_stem_info ().idealy_f_
;
378 Real lengthened
= paper_l ()->get_var ("beam_lengthened");
379 Real steep
= paper_l ()->get_var ("beam_steep_slope");
381 Real dx
= last_visible_stem ()->hpos_f () - first_visible_stem ()->hpos_f ();
382 Real dydx
= dy
&& dx
? dy
/dx
: 0;
384 if (((y
- first_ideal
> lengthened
) && (dydx
> steep
))
385 || ((y
+ dy
- last_ideal
> lengthened
) && (dydx
< -steep
)))
393 This neat trick is by Werner Lemberg,
394 damped = tanh (slope)
395 corresponds with some tables in [Wanske]
398 Beam::calc_slope_damping_f (Real dy
) const
400 SCM damp
= get_elt_property ("damping"); // remove?
401 int damping
= 1; // ugh.
402 if (gh_number_p (damp
))
403 damping
= gh_scm2int (damp
);
407 Real dx
= last_visible_stem ()->hpos_f ()
408 - first_visible_stem ()->hpos_f ();
409 Real dydx
= dy
&& dx
? dy
/dx
: 0;
410 dydx
= 0.6 * tanh (dydx
) / damping
;
417 Beam::calc_stem_y_f (Stem
* s
, Real y
, Real dy
) const
419 Real thick
= gh_scm2double (get_elt_property ("beam-thickness"));
420 int beam_multiplicity
= get_multiplicity ();
421 int stem_multiplicity
= (s
->flag_i () - 2) >? 0;
423 Real interbeam_f
= paper_l ()->interbeam_f (beam_multiplicity
);
424 Real x0
= first_visible_stem ()->hpos_f ();
425 Real dx
= last_visible_stem ()->hpos_f () - x0
;
426 Real stem_y
= (dy
&& dx
? (s
->hpos_f () - x0
) / dx
* dy
: 0) + y
;
429 Direction dir
= directional_element(this).get ();
430 Direction sdir
= directional_element (s
).get ();
436 * (thick
/ 2 + (beam_multiplicity
- 1) * interbeam_f
);
438 Staff_symbol_referencer_interface
me (s
);
439 Staff_symbol_referencer_interface
last (last_visible_stem ());
441 // huh, why not for first visible?
442 if (//(s != first_visible_stem ()) &&
443 me
.staff_symbol_l () != last
.staff_symbol_l ())
444 stem_y
+= directional_element (this).get ()
445 * (beam_multiplicity
- stem_multiplicity
) * interbeam_f
;
451 Beam::check_stem_length_f (Real y
, Real dy
) const
455 Direction dir
= directional_element (this).get ();
457 for (int i
=0; i
< stem_count (); i
++)
460 if (s
->invisible_b ())
463 Real stem_y
= calc_stem_y_f (s
, y
, dy
);
466 Stem_info info
= s
->calc_stem_info ();
468 // if (0 > info.maxy_f_ - stem_y)
469 shorten
= shorten
<? info
.maxy_f_
- stem_y
;
470 // if (0 < info.miny_f_ - stem_y)
471 lengthen
= lengthen
>? info
.miny_f_
- stem_y
;
474 if (lengthen
&& shorten
)
475 warning (_ ("weird beam vertical offset"));
477 /* when all stems are too short, normal stems win */
478 return dir
* ((shorten
) ? shorten
: lengthen
);
482 Hmm. At this time, beam position and slope are determined. Maybe,
483 stem directions and length should set to relative to the chord's
484 position of the beam. */
486 Beam::set_stem_length (Real y
, Real dy
)
488 Staff_symbol_referencer_interface
st (this);
489 Real half_space
= st
.staff_space ()/2;
490 for (int i
=0; i
< stem_count (); i
++)
493 if (s
->invisible_b ())
496 Real stem_y
= calc_stem_y_f (s
, y
, dy
);
498 /* caution: stem measures in staff-positions */
499 s
->set_stemend ((stem_y
+ calc_interstaff_dist (s
, this)) / half_space
);
504 [Ross] (simplification of)
505 Set dy complying with:
507 - thick / 2 + staffline_f / 2
508 - thick + staffline_f
512 Beam::quantise_dy_f (Real dy
) const
514 SCM quants
= ly_eval_str ("beam-height-quants");
517 scm_to_array (quants
, &a
);
521 Staff_symbol_referencer_interface
st (this);
522 Real staff_space
= st
.staff_space ();
524 Interval iv
= quantise_iv (a
, abs (dy
)/staff_space
) * staff_space
;
525 Real q
= (abs (dy
) - iv
[SMALLER
] <= iv
[BIGGER
] - abs (dy
))
529 return q
* sign (dy
);
533 Prevent interference from stafflines and beams.
534 See Documentation/tex/fonts.doc
536 We only need to quantise the (left) y-position of the beam,
537 since dy is quantised too.
538 if extend_b then stems must *not* get shorter
541 Beam::quantise_y_f (Real y
, Real dy
, int quant_dir
)
543 int multiplicity
= get_multiplicity ();
544 Staff_symbol_referencer_interface
st (this);
545 Real staff_space
= st
.staff_space ();
546 SCM quants
= scm_eval (gh_list (
547 ly_symbol2scm ("beam-vertical-position-quants"),
548 gh_int2scm (multiplicity
),
549 gh_double2scm (dy
/staff_space
),
552 scm_to_array (quants
, &a
);
556 Real up_y
= directional_element (this).get () * y
;
557 Interval iv
= quantise_iv (a
, up_y
/staff_space
) * staff_space
;
559 Real q
= up_y
- iv
[SMALLER
] <= iv
[BIGGER
] - up_y
560 ? iv
[SMALLER
] : iv
[BIGGER
];
562 q
= iv
[(Direction
)quant_dir
];
564 return q
* directional_element (this).get ();
568 Beam::set_beaming (Beaming_info_list
*beaming
)
571 for (int i
=0; i
< stem_count (); i
++)
575 if (stem (i
)->beam_count (d
) == 0)
576 stem (i
)->set_beaming ( beaming
->infos_
.elem (i
).beams_i_drul_
[d
],d
);
578 while (flip (&d
) != LEFT
);
585 beams to go with one stem.
591 Beam::stem_beams (Stem
*here
, Stem
*next
, Stem
*prev
) const
593 if ((next
&& !(next
->hpos_f () > here
->hpos_f ())) ||
594 (prev
&& !(prev
->hpos_f () < here
->hpos_f ())))
595 programming_error ("Beams are not left-to-right");
597 Real staffline_f
= paper_l ()->get_var ("stafflinethickness");
598 int multiplicity
= get_multiplicity ();
601 Real interbeam_f
= paper_l ()->interbeam_f (multiplicity
);
602 Real thick
= gh_scm2double (get_elt_property ("beam-thickness"));;
604 Real bdy
= interbeam_f
;
605 Real stemdx
= staffline_f
;
607 Real dx
= visible_stem_count () ?
608 last_visible_stem ()->hpos_f () - first_visible_stem ()->hpos_f ()
610 Real dy
= get_real ("height");
611 Real dydx
= dy
&& dx
? dy
/dx
: 0;
618 if (!here
->first_head ())
620 else if (here
->type_i ()== 1)
621 nw_f
= paper_l ()->get_var ("wholewidth");
622 else if (here
->type_i () == 2)
623 nw_f
= paper_l ()->get_var ("notewidth") * 0.8;
625 nw_f
= paper_l ()->get_var ("quartwidth");
628 Direction dir
= directional_element (this).get ();
630 /* half beams extending to the left. */
633 int lhalfs
= lhalfs
= here
->beam_count (LEFT
) - prev
->beam_count (RIGHT
);
634 int lwholebeams
= here
->beam_count (LEFT
) <? prev
->beam_count (RIGHT
) ;
636 Half beam should be one note-width,
637 but let's make sure two half-beams never touch
639 Real w
= here
->hpos_f () - prev
->hpos_f ();
642 if (lhalfs
) // generates warnings if not
643 a
= lookup_l ()->beam (dydx
, w
, thick
);
644 a
.translate (Offset (-w
, -w
* dydx
));
645 for (int j
= 0; j
< lhalfs
; j
++)
648 b
.translate_axis (-dir
* bdy
* (lwholebeams
+j
), Y_AXIS
);
649 leftbeams
.add_molecule (b
);
655 int rhalfs
= here
->beam_count (RIGHT
) - next
->beam_count (LEFT
);
656 int rwholebeams
= here
->beam_count (RIGHT
) <? next
->beam_count (LEFT
) ;
658 Real w
= next
->hpos_f () - here
->hpos_f ();
659 Molecule a
= lookup_l ()->beam (dydx
, w
+ stemdx
, thick
);
660 a
.translate_axis( - stemdx
/2, X_AXIS
);
664 SCM gap
= get_elt_property ("beam-gap");
665 if (gh_number_p (gap
))
667 int gap_i
= gh_scm2int ( (gap
));
668 int nogap
= rwholebeams
- gap_i
;
670 for (; j
< nogap
; j
++)
673 b
.translate_axis (-dir
* bdy
* j
, Y_AXIS
);
674 rightbeams
.add_molecule (b
);
676 // TODO: notehead widths differ for different types
679 a
= lookup_l ()->beam (dydx
, w
+ stemdx
, thick
);
682 for (; j
< rwholebeams
; j
++)
685 b
.translate (Offset (here
->invisible_b () ? 0 : gap_f
, -dir
* bdy
* j
));
686 rightbeams
.add_molecule (b
);
691 a
= lookup_l ()->beam (dydx
, w
, thick
);
693 for (; j
< rwholebeams
+ rhalfs
; j
++)
696 b
.translate_axis (- dir
* bdy
* j
, Y_AXIS
);
697 rightbeams
.add_molecule (b
);
701 leftbeams
.add_molecule (rightbeams
);
704 Does beam quanting think of the asymetry of beams?
705 Refpoint is on bottom of symbol. (FIXTHAT) --hwn.
712 Beam::do_brew_molecule_p () const
714 Molecule
*mol_p
= new Molecule
;
718 if (visible_stem_count ())
720 x0
= first_visible_stem ()->hpos_f ();
721 dx
= last_visible_stem ()->hpos_f () - x0
;
725 x0
= stem (0)->hpos_f ();
726 dx
= stem_top ()->hpos_f () - x0
;
730 Real dy
= get_real ("height");
731 Real dydx
= dy
&& dx
? dy
/dx
: 0;
732 Real y
= get_real ("y-position");
733 for (int j
=0; j
<stem_count (); j
++)
736 Stem
* prev
= (j
> 0)? stem (j
-1) : 0;
737 Stem
* next
= (j
< stem_count ()-1) ? stem (j
+1) :0;
739 Molecule sb
= stem_beams (i
, next
, prev
);
740 Real x
= i
->hpos_f ()-x0
;
741 sb
.translate (Offset (x
, x
* dydx
+ y
));
742 mol_p
->add_molecule (sb
);
744 mol_p
->translate_axis (x0
745 - spanned_drul_
[LEFT
]->relative_coordinate (0, X_AXIS
), X_AXIS
);
751 Beam::forced_stem_count () const
754 for (int i
=0; i
< stem_count (); i
++)
758 if (s
->invisible_b ())
761 if (((int)s
->chord_start_f ())
762 && (s
->get_direction () != s
->get_default_dir ()))
771 TODO: Fix this class. This is wildly inefficient.
772 And it sux. Yet another array/list 'interface'.
775 Beam::stem (int i
) const
777 return Group_interface__extract_elements ((Beam
*) this, (Stem
*) 0, "stems")[i
];
781 Beam::stem_count () const
783 Group_interface
gi (this, "stems");
788 Beam::stem_top () const
790 SCM s
= get_elt_property ("stems");
792 return gh_pair_p (s
) ? dynamic_cast<Stem
*> (unsmob_element (gh_car (s
))) : 0;
794 //Group_interface__extract_elements ((Beam*) this, (Stem*) 0, "stems")[stem_count () - 1];
799 Beam::visible_stem_count () const
802 for (int i
= 0; i
< stem_count (); i
++)
804 if (!stem (i
)->invisible_b ())
811 Beam::first_visible_stem () const
813 for (int i
= 0; i
< stem_count (); i
++)
816 if (!s
->invisible_b ())
823 Beam::last_visible_stem () const
825 for (int i
= stem_count (); i
> 0; i
--)
827 Stem
* s
= stem (i
- 1);
828 if (!s
->invisible_b ())