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
16 remove *-hs variables.
20 #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"
38 Pointer_group_interface
g (this, "stems");
41 set_elt_property ("height", gh_int2scm (0)); // ugh.
42 set_elt_property ("y-position" ,gh_int2scm (0));
46 Beam::add_stem (Stem
*s
)
48 Pointer_group_interface
gi (this, "stems");
51 s
->add_dependency (this);
53 assert (!s
->beam_l ());
54 s
->set_elt_pointer ("beam", self_scm_
);
56 add_bound_item (this, s
);
60 Beam::get_multiplicity () const
63 for (SCM s
= get_elt_pointer ("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'.]
81 GLUE_SCORE_ELEMENT(Beam
,before_line_breaking
);
83 Beam::member_before_line_breaking ()
86 if (visible_stem_count () < 2)
88 warning (_ ("beam has less than two stems"));
92 if (!Directional_element_interface (this).get ())
93 Directional_element_interface (this).set (get_default_dir ());
96 set_stem_directions ();
106 Beam::get_default_dir () const
108 Drul_array
<int> total
;
109 total
[UP
] = total
[DOWN
] = 0;
110 Drul_array
<int> count
;
111 count
[UP
] = count
[DOWN
] = 0;
114 for (int i
=0; i
<stem_count (); i
++)
115 do { // HUH -- waar slaat dit op?
117 Direction sd
= Directional_element_interface (s
).get ();
118 int current
= sd
? (1 + d
* sd
)/2
119 : s
->get_center_distance ((Direction
)-d
);
127 } while (flip(&d
) != DOWN
);
130 SCM s
= scm_eval (gh_list (ly_symbol2scm ("beam-dir-algorithm"),
131 ly_quote_scm (gh_cons (gh_int2scm (count
[UP
]),
132 gh_int2scm (count
[DOWN
]))),
133 ly_quote_scm (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 from paper
142 return (Direction
)(int)
143 paper_l ()->get_var ("stem_default_neutral_direction");
148 Set all stems with non-forced direction to beam direction.
149 Urg: non-forced should become `without/with unforced' direction,
150 once stem gets cleaned-up.
153 Beam::set_stem_directions ()
155 Direction d
= Directional_element_interface (this).get ();
156 for (int i
=0; i
<stem_count (); i
++)
159 SCM force
= s
->remove_elt_property ("dir-forced");
160 if (!gh_boolean_p (force
) || !gh_scm2bool (force
))
161 Directional_element_interface (s
).set (d
);
168 if (!auto_knee ("auto-interstaff-knee-gap", true))
169 auto_knee ("auto-knee-gap", false);
173 Simplistic auto-knees; only consider vertical gap between two
176 `Forced' stem directions are ignored. If you don't want auto-knees,
177 don't set, or unset autoKneeGap/autoInterstaffKneeGap.
180 Beam::auto_knee (String gap_str
, bool interstaff_b
)
184 SCM gap
= get_elt_property (gap_str
);
185 Direction d
= Directional_element_interface (this).get ();
187 if (gh_number_p (gap
))
189 int auto_gap_i
= gh_scm2int (gap
);
190 for (int i
=1; i
< stem_count (); i
++)
192 bool is_b
= (bool)(calc_interstaff_dist (stem (i
), this)
193 - calc_interstaff_dist (stem (i
-1), this));
194 int l_y
= (int)(stem (i
-1)->head_positions()[d
])
195 + (int)calc_interstaff_dist (stem (i
-1), this);
196 int r_y
= (int)(stem (i
)->head_positions()[d
])
197 + (int)calc_interstaff_dist (stem (i
), this);
198 int gap_i
= r_y
- l_y
;
200 if ((abs (gap_i
) >= auto_gap_i
) && (!interstaff_b
|| is_b
))
202 knee_y
= (r_y
+ l_y
) / 2;
210 for (int i
=0; i
< stem_count (); i
++)
213 int y
= (int)(stem (i
)->head_positions()[d
])
214 + (int)calc_interstaff_dist (s
, this);
217 Directional_element_interface (s
).set (y
< knee_y
? UP
: DOWN
);
218 s
->set_elt_property ("dir-forced", SCM_BOOL_T
);
225 Set stem's shorten property if unset.
227 take some y-position (chord/beam/nearest?) into account
228 scmify forced-fraction
231 Beam::set_stem_shorten ()
233 if (!visible_stem_count ())
236 Real forced_fraction
= forced_stem_count () / visible_stem_count ();
237 if (forced_fraction
< 0.5)
240 int multiplicity
= get_multiplicity ();
243 SCM shorten
= scm_eval (ly_symbol2scm ("beamed-stem-shorten"));
245 if (shorten
== SCM_EOL
)
248 int sz
= scm_ilength (shorten
);
250 Staff_symbol_referencer_interface
st (this);
251 Real staff_space
= st
.staff_space ();
252 SCM shorten_elt
= scm_list_ref (shorten
, gh_int2scm (multiplicity
<? (sz
- 1)));
253 Real shorten_f
= gh_scm2double (shorten_elt
) * staff_space
;
255 /* cute, but who invented this -- how to customise ? */
256 if (forced_fraction
< 1)
259 for (int i
=0; i
< stem_count (); i
++)
262 if (s
->invisible_b ())
264 if (gh_number_p (s
->get_elt_property ("shorten")))
265 s
->set_elt_property ("shorten", gh_double2scm (shorten_f
));
270 Set elt properties height and y-position if not set.
271 Adjust stem lengths to reach beam.
273 GLUE_SCORE_ELEMENT(Beam
,after_line_breaking
);
275 Beam::member_after_line_breaking ()
277 /* first, calculate y, dy */
279 calc_default_position_and_height (&y
, &dy
);
280 if (visible_stem_count ())
282 if (suspect_slope_b (y
, dy
))
285 Real damped_dy
= calc_slope_damping_f (dy
);
286 Real quantised_dy
= quantise_dy_f (damped_dy
);
288 y
+= (dy
- quantised_dy
) / 2;
292 until here, we used only stem_info, which acts as if dir=up
294 y
*= Directional_element_interface (this).get ();
295 dy
*= Directional_element_interface (this).get ();
297 Staff_symbol_referencer_interface
st (this);
298 Real half_space
= st
.staff_space () / 2;
300 /* check for user-override of dy */
301 SCM s
= remove_elt_property ("height-hs");
304 dy
= gh_scm2double (s
) * half_space
;
306 set_elt_property ("height", gh_double2scm (dy
));
308 /* check for user-override of y */
309 s
= remove_elt_property ("y-position-hs");
312 y
= gh_scm2double (s
) * half_space
;
316 /* we can modify y, so we should quantise y */
317 Real y_shift
= check_stem_length_f (y
, dy
);
319 y
= quantise_y_f (y
, dy
, 0);
320 set_stem_length (y
, dy
);
321 y_shift
= check_stem_length_f (y
, dy
);
323 if (y_shift
> half_space
/ 4)
328 for significantly lengthened or shortened stems,
329 request quanting the other way.
332 if (abs (y_shift
) > half_space
/ 2)
333 quant_dir
= sign (y_shift
) * Directional_element_interface (this).get ();
334 y
= quantise_y_f (y
, dy
, quant_dir
);
337 // UGH. Y is not in staff position unit?
338 // Ik dacht datwe daar juist van weg wilden?
339 set_stem_length (y
, dy
);
340 set_elt_property ("y-position", gh_double2scm (y
));
343 return SCM_UNDEFINED
;
347 See Documentation/tex/fonts.doc
350 Beam::calc_default_position_and_height (Real
* y
, Real
* dy
) const
354 if (visible_stem_count () <= 1)
357 Real first_ideal
= first_visible_stem ()->calc_stem_info ().idealy_f_
;
358 if (first_ideal
== last_visible_stem ()->calc_stem_info ().idealy_f_
)
365 Array
<Offset
> ideals
;
366 Real x0
= first_visible_stem ()->relative_coordinate (0, X_AXIS
);
367 for (int i
=0; i
< stem_count (); i
++)
370 if (s
->invisible_b ())
372 ideals
.push (Offset (s
->relative_coordinate (0, X_AXIS
) - x0
,
373 s
->calc_stem_info ().idealy_f_
));
376 minimise_least_squares (&dydx
, y
, ideals
); // duh, takes references
378 Real dx
= last_visible_stem ()->relative_coordinate (0, X_AXIS
) - x0
;
383 Beam::suspect_slope_b (Real y
, Real dy
) const
385 /* first, calculate y, dy */
387 steep slope running against lengthened stem is suspect
389 Real first_ideal
= first_visible_stem ()->calc_stem_info ().idealy_f_
;
390 Real last_ideal
= last_visible_stem ()->calc_stem_info ().idealy_f_
;
391 Real lengthened
= paper_l ()->get_var ("beam_lengthened");
392 Real steep
= paper_l ()->get_var ("beam_steep_slope");
394 Real dx
= last_visible_stem ()->relative_coordinate (0, X_AXIS
) - first_visible_stem ()->relative_coordinate (0, X_AXIS
);
395 Real dydx
= dy
&& dx
? dy
/dx
: 0;
397 if (((y
- first_ideal
> lengthened
) && (dydx
> steep
))
398 || ((y
+ dy
- last_ideal
> lengthened
) && (dydx
< -steep
)))
406 This neat trick is by Werner Lemberg,
407 damped = tanh (slope)
408 corresponds with some tables in [Wanske]
411 Beam::calc_slope_damping_f (Real dy
) const
413 SCM damp
= get_elt_property ("damping"); // remove?
414 int damping
= 1; // ugh.
415 if (gh_number_p (damp
))
416 damping
= gh_scm2int (damp
);
420 Real dx
= last_visible_stem ()->relative_coordinate (0, X_AXIS
)
421 - first_visible_stem ()->relative_coordinate (0, X_AXIS
);
422 Real dydx
= dy
&& dx
? dy
/dx
: 0;
423 dydx
= 0.6 * tanh (dydx
) / damping
;
430 Beam::calc_stem_y_f (Stem
* s
, Real y
, Real dy
) const
432 Real thick
= gh_scm2double (get_elt_property ("beam-thickness"));
433 thick
*= paper_l ()->get_var ("staffspace");
435 int beam_multiplicity
= get_multiplicity ();
436 int stem_multiplicity
= (s
->flag_i () - 2) >? 0;
438 Real interbeam_f
= paper_l ()->interbeam_f (beam_multiplicity
);
439 Real x0
= first_visible_stem ()->relative_coordinate (0, X_AXIS
);
440 Real dx
= last_visible_stem ()->relative_coordinate (0, X_AXIS
) - x0
;
441 Real stem_y
= (dy
&& dx
? (s
->relative_coordinate (0, X_AXIS
) - x0
) / dx
* dy
: 0) + y
;
444 Direction dir
= Directional_element_interface(this).get ();
445 Direction sdir
= Directional_element_interface (s
).get ();
451 * (thick
/ 2 + (beam_multiplicity
- 1) * interbeam_f
);
453 Staff_symbol_referencer_interface
me (s
);
454 Staff_symbol_referencer_interface
last (last_visible_stem ());
456 // huh, why not for first visible?
457 if (//(s != first_visible_stem ()) &&
458 me
.staff_symbol_l () != last
.staff_symbol_l ())
459 stem_y
+= Directional_element_interface (this).get ()
460 * (beam_multiplicity
- stem_multiplicity
) * interbeam_f
;
466 Beam::check_stem_length_f (Real y
, Real dy
) const
470 Direction dir
= Directional_element_interface (this).get ();
472 for (int i
=0; i
< stem_count (); i
++)
475 if (s
->invisible_b ())
478 Real stem_y
= calc_stem_y_f (s
, y
, dy
);
481 Stem_info info
= s
->calc_stem_info ();
483 // if (0 > info.maxy_f_ - stem_y)
484 shorten
= shorten
<? info
.maxy_f_
- stem_y
;
485 // if (0 < info.miny_f_ - stem_y)
486 lengthen
= lengthen
>? info
.miny_f_
- stem_y
;
489 if (lengthen
&& shorten
)
490 warning (_ ("weird beam vertical offset"));
492 /* when all stems are too short, normal stems win */
493 return dir
* ((shorten
) ? shorten
: lengthen
);
497 Hmm. At this time, beam position and slope are determined. Maybe,
498 stem directions and length should set to relative to the chord's
499 position of the beam. */
501 Beam::set_stem_length (Real y
, Real dy
)
503 Staff_symbol_referencer_interface
st (this);
504 Real half_space
= st
.staff_space ()/2;
505 for (int i
=0; i
< stem_count (); i
++)
508 if (s
->invisible_b ())
511 Real stem_y
= calc_stem_y_f (s
, y
, dy
);
513 /* caution: stem measures in staff-positions */
514 s
->set_stemend ((stem_y
+ calc_interstaff_dist (s
, this)) / half_space
);
519 [Ross] (simplification of)
520 Set dy complying with:
522 - thick / 2 + staffline_f / 2
523 - thick + staffline_f
527 Beam::quantise_dy_f (Real dy
) const
530 for (SCM s
= scm_eval (ly_symbol2scm ("beam-height-quants")); s
!=SCM_EOL
; s
= gh_cdr (s
))
531 a
.push (gh_scm2double (gh_car (s
)));
536 Staff_symbol_referencer_interface
st (this);
537 Real staff_space
= st
.staff_space ();
539 Interval iv
= quantise_iv (a
, abs (dy
)/staff_space
) * staff_space
;
540 Real q
= (abs (dy
) - iv
[SMALLER
] <= iv
[BIGGER
] - abs (dy
))
544 return q
* sign (dy
);
548 Prevent interference from stafflines and beams.
549 See Documentation/tex/fonts.doc
551 We only need to quantise the (left) y-position of the beam,
552 since dy is quantised too.
553 if extend_b then stems must *not* get shorter
556 Beam::quantise_y_f (Real y
, Real dy
, int quant_dir
)
558 int multiplicity
= get_multiplicity ();
559 Staff_symbol_referencer_interface
st (this);
560 Real staff_space
= st
.staff_space ();
561 SCM quants
= scm_eval (gh_list (ly_symbol2scm ("beam-vertical-position-quants"),
562 gh_int2scm (multiplicity
),
563 gh_double2scm (dy
/staff_space
),
568 for (; quants
!= SCM_EOL
; quants
= gh_cdr (quants
))
569 a
.push (gh_scm2double (gh_car (quants
)));
574 Real up_y
= Directional_element_interface (this).get () * y
;
575 Interval iv
= quantise_iv (a
, up_y
/staff_space
) * staff_space
;
577 Real q
= up_y
- iv
[SMALLER
] <= iv
[BIGGER
] - up_y
578 ? iv
[SMALLER
] : iv
[BIGGER
];
580 q
= iv
[(Direction
)quant_dir
];
582 return q
* Directional_element_interface (this).get ();
586 Beam::set_beaming (Beaming_info_list
*beaming
)
589 for (int i
=0; i
< stem_count (); i
++)
593 if (stem (i
)->beam_count (d
) == 0)
594 stem (i
)->set_beaming ( beaming
->infos_
.elem (i
).beams_i_drul_
[d
],d
);
596 while (flip (&d
) != LEFT
);
603 beams to go with one stem.
609 Beam::stem_beams (Stem
*here
, Stem
*next
, Stem
*prev
) const
611 if ((next
&& !(next
->relative_coordinate (0, X_AXIS
) > here
->relative_coordinate (0, X_AXIS
))) ||
612 (prev
&& !(prev
->relative_coordinate (0, X_AXIS
) < here
->relative_coordinate (0, X_AXIS
))))
613 programming_error ("Beams are not left-to-right");
615 Real staffline_f
= paper_l ()->get_var ("stafflinethickness");
616 int multiplicity
= get_multiplicity ();
619 Real interbeam_f
= paper_l ()->interbeam_f (multiplicity
);
620 Real thick
= gh_scm2double (get_elt_property ("beam-thickness"));
621 thick
*= paper_l ()->get_var ("staffspace");
623 Real bdy
= interbeam_f
;
624 Real stemdx
= staffline_f
;
626 Real dx
= visible_stem_count () ?
627 last_visible_stem ()->relative_coordinate (0, X_AXIS
) - first_visible_stem ()->relative_coordinate (0, X_AXIS
)
629 Real dy
= gh_scm2double (get_elt_property ("height"));
630 Real dydx
= dy
&& dx
? dy
/dx
: 0;
637 if (!here
->first_head ())
639 else if (here
->type_i ()== 1)
640 nw_f
= paper_l ()->get_var ("wholewidth");
641 else if (here
->type_i () == 2)
642 nw_f
= paper_l ()->get_var ("notewidth") * 0.8;
644 nw_f
= paper_l ()->get_var ("quartwidth");
647 Direction dir
= Directional_element_interface (this).get ();
649 /* half beams extending to the left. */
652 int lhalfs
= lhalfs
= here
->beam_count (LEFT
) - prev
->beam_count (RIGHT
);
653 int lwholebeams
= here
->beam_count (LEFT
) <? prev
->beam_count (RIGHT
) ;
655 Half beam should be one note-width,
656 but let's make sure two half-beams never touch
658 Real w
= here
->relative_coordinate (0, X_AXIS
) - prev
->relative_coordinate (0, X_AXIS
);
661 if (lhalfs
) // generates warnings if not
662 a
= lookup_l ()->beam (dydx
, w
, thick
);
663 a
.translate (Offset (-w
, -w
* dydx
));
664 for (int j
= 0; j
< lhalfs
; j
++)
667 b
.translate_axis (-dir
* bdy
* (lwholebeams
+j
), Y_AXIS
);
668 leftbeams
.add_molecule (b
);
674 int rhalfs
= here
->beam_count (RIGHT
) - next
->beam_count (LEFT
);
675 int rwholebeams
= here
->beam_count (RIGHT
) <? next
->beam_count (LEFT
) ;
677 Real w
= next
->relative_coordinate (0, X_AXIS
) - here
->relative_coordinate (0, X_AXIS
);
678 Molecule a
= lookup_l ()->beam (dydx
, w
+ stemdx
, thick
);
679 a
.translate_axis( - stemdx
/2, X_AXIS
);
683 SCM gap
= get_elt_property ("beam-gap");
684 if (gh_number_p (gap
))
686 int gap_i
= gh_scm2int ( (gap
));
687 int nogap
= rwholebeams
- gap_i
;
689 for (; j
< nogap
; j
++)
692 b
.translate_axis (-dir
* bdy
* j
, Y_AXIS
);
693 rightbeams
.add_molecule (b
);
695 // TODO: notehead widths differ for different types
698 a
= lookup_l ()->beam (dydx
, w
+ stemdx
, thick
);
701 for (; j
< rwholebeams
; j
++)
704 b
.translate (Offset (here
->invisible_b () ? 0 : gap_f
, -dir
* bdy
* j
));
705 rightbeams
.add_molecule (b
);
710 a
= lookup_l ()->beam (dydx
, w
, thick
);
712 for (; j
< rwholebeams
+ rhalfs
; j
++)
715 b
.translate_axis (- dir
* bdy
* j
, Y_AXIS
);
716 rightbeams
.add_molecule (b
);
720 leftbeams
.add_molecule (rightbeams
);
723 Does beam quanting think of the asymetry of beams?
724 Refpoint is on bottom of symbol. (FIXTHAT) --hwn.
729 GLUE_SCORE_ELEMENT(Beam
,brew_molecule
);
731 Beam::member_brew_molecule () const
737 if (visible_stem_count ())
739 x0
= first_visible_stem ()->relative_coordinate (0, X_AXIS
);
740 dx
= last_visible_stem ()->relative_coordinate (0, X_AXIS
) - x0
;
744 x0
= stem (0)->relative_coordinate (0, X_AXIS
);
745 dx
= stem_top ()->relative_coordinate (0, X_AXIS
) - x0
;
749 Real dy
= gh_scm2double (get_elt_property ("height"));
750 Real dydx
= dy
&& dx
? dy
/dx
: 0;
751 Real y
= gh_scm2double (get_elt_property ("y-position"));
752 for (int j
=0; j
<stem_count (); j
++)
755 Stem
* prev
= (j
> 0)? stem (j
-1) : 0;
756 Stem
* next
= (j
< stem_count ()-1) ? stem (j
+1) :0;
758 Molecule sb
= stem_beams (i
, next
, prev
);
759 Real x
= i
->relative_coordinate (0, X_AXIS
)-x0
;
760 sb
.translate (Offset (x
, x
* dydx
+ y
));
761 mol
.add_molecule (sb
);
763 mol
.translate_axis (x0
764 - get_bound (LEFT
)->relative_coordinate (0, X_AXIS
), X_AXIS
);
766 return mol
.create_scheme ();
770 Beam::forced_stem_count () const
773 for (int i
=0; i
< stem_count (); i
++)
777 if (s
->invisible_b ())
780 if (((int)s
->chord_start_f ())
781 && (s
->get_direction () != s
->get_default_dir ()))
788 TODO: Fix this class. This is wildly inefficient.
789 And it sux. Yet another array/list 'interface'.
792 Beam::stem (int i
) const
794 return Pointer_group_interface__extract_elements ((Beam
*) this, (Stem
*) 0, "stems")[i
];
798 Beam::stem_count () const
800 Pointer_group_interface
gi (this, "stems");
805 Beam::stem_top () const
807 SCM s
= get_elt_pointer ("stems");
809 return gh_pair_p (s
) ? dynamic_cast<Stem
*> (unsmob_element (gh_car (s
))) : 0;
814 Beam::visible_stem_count () const
817 for (int i
= 0; i
< stem_count (); i
++)
819 if (!stem (i
)->invisible_b ())
826 Beam::first_visible_stem () const
828 for (int i
= 0; i
< stem_count (); i
++)
831 if (!s
->invisible_b ())
838 Beam::last_visible_stem () const
840 for (int i
= stem_count (); i
> 0; i
--)
842 Stem
* s
= stem (i
- 1);
843 if (!s
->invisible_b ())
852 handle rest under beam (do_post: beams are calculated now)
853 what about combination of collisions and rest under beam.
857 rest -> stem -> beam -> interpolate_y_position ()
860 Beam::rest_collision_callback (Score_element
const *rest
, Axis a
)
862 assert (a
== Y_AXIS
);
864 Score_element
* st
= unsmob_element (rest
->get_elt_pointer ("stem"));
865 Stem
* stem
= dynamic_cast<Stem
*> (st
);
868 Beam
* beam
= dynamic_cast<Beam
*> (unsmob_element (stem
->get_elt_pointer ("beam")));
869 if (!beam
|| !beam
->visible_stem_count ())
872 // make callback for rest from this.
877 // todo: make sure this calced already.
878 SCM s
= beam
->get_elt_property ("height");
880 beam_dy
= gh_scm2double (s
);
882 s
= beam
->get_elt_property ("y-position");
884 beam_y
= gh_scm2double (s
);
886 Real x0
= beam
->first_visible_stem ()->relative_coordinate (0, X_AXIS
);
887 Real dx
= beam
->last_visible_stem ()->relative_coordinate (0, X_AXIS
) - x0
;
888 Real dydx
= beam_dy
&& dx
? beam_dy
/dx
: 0;
890 Direction d
= stem
->get_direction ();
891 Real beamy
= (stem
->relative_coordinate (0, X_AXIS
) - x0
) * dydx
+ beam_y
;
893 Staff_symbol_referencer_interface
si (rest
);
895 Real staff_space
= si
.staff_space ();
896 Real rest_dim
= rest
->extent (Y_AXIS
)[d
]*2.0 / staff_space
;
899 = gh_scm2double (rest
->get_elt_property ("minimum-beam-collision-distance"));
901 minimum_dist
+ -d
* (beamy
- rest_dim
) >? 0;
903 int stafflines
= si
.line_count ();
905 // move discretely by half spaces.
906 int discrete_dist
= int (ceil (dist
));
908 // move by whole spaces inside the staff.
909 if (discrete_dist
< stafflines
+1)
910 discrete_dist
= int (ceil (discrete_dist
/ 2.0)* 2.0);
912 return (-d
* discrete_dist
);