2 beam-quanting.cc -- implement Beam quanting functions
4 source file of the GNU LilyPond music typesetter
6 (c) 1997--2007 Han-Wen Nienhuys <hanwen@xs4all.nl>
7 Jan Nieuwenhuizen <janneke@gnu.org>
16 #include "align-interface.hh"
17 #include "international.hh"
18 #include "output-def.hh"
19 #include "pointer-group-interface.hh"
20 #include "staff-symbol-referencer.hh"
26 get_detail (SCM alist
, SCM sym
, Real def
)
28 SCM entry
= scm_assq (sym
, alist
);
30 if (scm_is_pair (entry
))
31 return robust_scm2double (scm_cdr (entry
), def
);
36 Beam_quant_parameters::fill (Grob
*him
)
38 SCM details
= him
->get_property ("details");
41 TODO: The default values should be copied to define-grobs.scm.
43 INTER_QUANT_PENALTY
= get_detail (details
, ly_symbol2scm ("inter-quant-penalty"), 1000.0);
44 SECONDARY_BEAM_DEMERIT
= get_detail (details
, ly_symbol2scm ("secondary-beam-demerit"), 10.0);
45 STEM_LENGTH_DEMERIT_FACTOR
= get_detail (details
, ly_symbol2scm ("stem-length-demerit-factor"), 5);
46 REGION_SIZE
= get_detail (details
, ly_symbol2scm ("region-size"), 2);
47 BEAM_EPS
= get_detail (details
, ly_symbol2scm ("beam-eps"), 1e-3);
48 STEM_LENGTH_LIMIT_PENALTY
= get_detail (details
, ly_symbol2scm ("stem-length-limit-penalty"), 5000);
49 DAMPING_DIRECTION_PENALTY
= get_detail (details
, ly_symbol2scm ("damping-direction-penalty"), 800);
50 HINT_DIRECTION_PENALTY
= get_detail (details
, ly_symbol2scm ("hint-direction-penalty"), 20);
51 MUSICAL_DIRECTION_FACTOR
= get_detail (details
, ly_symbol2scm ("musical-direction-factor"), 400);
52 IDEAL_SLOPE_FACTOR
= get_detail (details
, ly_symbol2scm ("ideal-slope-factor"), 10);
53 ROUND_TO_ZERO_SLOPE
= get_detail (details
, ly_symbol2scm ("round-to-zero-slope"), 0.02);
57 shrink_extra_weight (Real x
, Real fac
)
59 return fabs (x
) * ((x
< 0) ? fac
: 1.0);
68 #if DEBUG_BEAM_SCORING
76 - Make all demerits customisable
78 - One sensible check per demerit (what's this --hwn)
80 - Add demerits for quants per se, as to forbid a specific quant
85 best_quant_score_idx (vector
<Quant_score
> const &qscores
)
89 for (vsize i
= qscores
.size (); i
--;)
91 if (qscores
[i
].demerits
< best
)
93 best
= qscores
[i
].demerits
;
101 MAKE_SCHEME_CALLBACK (Beam
, quanting
, 2);
103 Beam::quanting (SCM smob
, SCM posns
)
105 Grob
*me
= unsmob_grob (smob
);
107 Beam_quant_parameters parameters
;
108 parameters
.fill (me
);
110 Real yl
= scm_to_double (scm_car (posns
));
111 Real yr
= scm_to_double (scm_cdr (posns
));
114 Calculations are relative to a unit-scaled staff, i.e. the quants are
115 divided by the current staff_space.
117 Real ss
= Staff_symbol_referencer::staff_space (me
);
118 Real thickness
= Beam::get_thickness (me
) / ss
;
119 Real slt
= Staff_symbol_referencer::line_thickness (me
) / ss
;
121 Real dy_mus
= robust_scm2double (me
->get_property ("least-squares-dy"), 0);
123 Real sit
= (thickness
- slt
) / 2;
125 Real hang
= 1.0 - (thickness
- slt
) / 2;
126 Real quants
[] = {straddle
, sit
, inter
, hang
};
128 int num_quants
= int (sizeof (quants
) / sizeof (Real
));
129 vector
<Real
> quantsl
;
130 vector
<Real
> quantsr
;
133 going to REGION_SIZE == 2, yields another 0.6 second with
136 (result indexes between 70 and 575) ? --hwn.
141 Do stem computations. These depend on YL and YR linearly, so we can
142 precompute for every stem 2 factors.
145 = extract_grob_array (me
, "stems");
146 vector
<Stem_info
> stem_infos
;
147 vector
<Real
> base_lengths
;
148 vector
<Real
> stem_xposns
;
150 Drul_array
<bool> dirs_found (0, 0);
152 for (int a
= 2; a
--;)
153 common
[a
] = common_refpoint_of_array (stems
, me
, Axis (a
));
155 Grob
*fvs
= first_normal_stem (me
);
156 Grob
*lvs
= last_normal_stem (me
);
157 Real xl
= fvs
? fvs
->relative_coordinate (common
[X_AXIS
], X_AXIS
) : 0.0;
158 Real xr
= fvs
? lvs
->relative_coordinate (common
[X_AXIS
], X_AXIS
) : 0.0;
161 We store some info to quickly interpolate. The stemlength are
162 affine linear in YL and YR. If YL == YR == 0, then we might have
163 stem_y != 0.0, when we're cross staff.
166 for (vsize i
= 0; i
< stems
.size (); i
++)
170 Stem_info
si (Stem::get_stem_info (s
));
172 stem_infos
.push_back (si
);
173 dirs_found
[stem_infos
.back ().dir_
] = true;
175 bool f
= to_boolean (s
->get_property ("french-beaming"))
176 && s
!= lvs
&& s
!= fvs
;
178 if (Stem::is_normal_stem (s
))
180 base_lengths
.push_back (calc_stem_y (me
, s
, common
, xl
, xr
, CENTER
,
181 Interval (0, 0), f
) / ss
);
185 base_lengths
.push_back (0);
188 stem_xposns
.push_back (s
->relative_coordinate (common
[X_AXIS
], X_AXIS
));
190 bool xstaff
= Align_interface::has_interface (common
[Y_AXIS
]);
192 Direction ldir
= Direction (stem_infos
[0].dir_
);
193 Direction rdir
= Direction (stem_infos
.back ().dir_
);
194 bool is_knee
= dirs_found
[LEFT
] && dirs_found
[RIGHT
];
196 int region_size
= (int) parameters
.REGION_SIZE
;
199 Knees are harder, lets try some more possibilities for knees.
205 Asymetry ? should run to <= region_size ?
207 for (int i
= -region_size
; i
< region_size
; i
++)
208 for (int j
= 0; j
< num_quants
; j
++)
210 quantsl
.push_back (i
+ quants
[j
] + int (yl
));
211 quantsr
.push_back (i
+ quants
[j
] + int (yr
));
214 vector
<Quant_score
> qscores
;
216 for (vsize l
= 0; l
< quantsl
.size (); l
++)
217 for (vsize r
= 0; r
< quantsr
.size (); r
++)
224 qscores
.push_back (qs
);
227 /* This is a longish function, but we don't separate this out into
228 neat modular separate subfunctions, as the subfunctions would be
229 called for many values of YL, YR. By precomputing various
230 parameters outside of the loop, we can save a lot of time. */
231 for (vsize i
= qscores
.size (); i
--;)
233 Real d
= score_slopes_dy (qscores
[i
].yl
, qscores
[i
].yr
,
236 xstaff
, ¶meters
);
237 qscores
[i
].demerits
+= d
;
239 #if DEBUG_BEAM_SCORING
240 qscores
[i
].score_card_
+= to_string ("S%.2f", d
);
244 Real rad
= Staff_symbol_referencer::staff_radius (me
);
245 Drul_array
<int> edge_beam_counts
246 (Stem::beam_multiplicity (stems
[0]).length () + 1,
247 Stem::beam_multiplicity (stems
.back ()).length () + 1);
249 Real beam_translation
= get_beam_translation (me
) / ss
;
251 Real reasonable_score
= (is_knee
) ? 200000 : 100;
252 for (vsize i
= qscores
.size (); i
--;)
253 if (qscores
[i
].demerits
< reasonable_score
)
255 Real d
= score_forbidden_quants (qscores
[i
].yl
, qscores
[i
].yr
,
256 rad
, slt
, thickness
, beam_translation
,
257 edge_beam_counts
, ldir
, rdir
, ¶meters
);
258 qscores
[i
].demerits
+= d
;
260 #if DEBUG_BEAM_SCORING
261 qscores
[i
].score_card_
+= to_string (" F %.2f", d
);
265 for (vsize i
= qscores
.size (); i
--;)
266 if (qscores
[i
].demerits
< reasonable_score
)
268 Real d
= score_stem_lengths (stems
, stem_infos
,
269 base_lengths
, stem_xposns
,
272 qscores
[i
].yl
, qscores
[i
].yr
, ¶meters
);
273 qscores
[i
].demerits
+= d
;
275 #if DEBUG_BEAM_SCORING
276 qscores
[i
].score_card_
+= to_string (" L %.2f", d
);
280 int best_idx
= best_quant_score_idx (qscores
);
282 #if DEBUG_BEAM_SCORING
283 SCM inspect_quants
= me
->get_property ("inspect-quants");
284 if (to_boolean (me
->layout ()->lookup_variable (ly_symbol2scm ("debug-beam-scoring")))
285 && scm_is_pair (inspect_quants
))
287 Drul_array
<Real
> ins
= ly_scm2interval (inspect_quants
);
290 for (vsize i
= 0; i
< qscores
.size (); i
++)
292 Real d
= fabs (qscores
[i
].yl
- ins
[LEFT
]) + fabs (qscores
[i
].yr
- ins
[RIGHT
]);
300 programming_error ("cannot find quant");
304 Interval final_positions
;
307 warning (_ ("no feasible beam position"));
308 final_positions
= Interval (0, 0);
312 final_positions
= Drul_array
<Real
> (qscores
[best_idx
].yl
,
313 qscores
[best_idx
].yr
);
316 #if DEBUG_BEAM_SCORING
318 && to_boolean (me
->layout ()->lookup_variable (ly_symbol2scm ("debug-beam-scoring"))))
320 qscores
[best_idx
].score_card_
+= to_string ("i%d", best_idx
);
323 me
->set_property ("quant-score",
324 ly_string2scm (qscores
[best_idx
].score_card_
));
328 return ly_interval2scm (final_positions
);
332 Beam::score_stem_lengths (vector
<Grob
*> const &stems
,
333 vector
<Stem_info
> const &stem_infos
,
334 vector
<Real
> const &base_stem_ys
,
335 vector
<Real
> const &stem_xs
,
340 Beam_quant_parameters
const *parameters
)
342 Real limit_penalty
= parameters
->STEM_LENGTH_LIMIT_PENALTY
;
343 Drul_array
<Real
> score (0, 0);
344 Drul_array
<int> count (0, 0);
346 for (vsize i
= 0; i
< stems
.size (); i
++)
349 if (!Stem::is_normal_stem (s
))
354 Real beam_y
= dx
? yr
* (x
- xl
) / dx
+ yl
* (xr
- x
) / dx
: (yr
+ yl
) / 2;
355 Real current_y
= beam_y
+ base_stem_ys
[i
];
356 Real length_pen
= parameters
->STEM_LENGTH_DEMERIT_FACTOR
;
358 Stem_info info
= stem_infos
[i
];
359 Direction d
= info
.dir_
;
361 score
[d
] += limit_penalty
* max (0.0, (d
* (info
.shortest_y_
- current_y
)));
363 Real ideal_diff
= d
* (current_y
- info
.ideal_y_
);
364 Real ideal_score
= shrink_extra_weight (ideal_diff
, 1.5);
366 /* We introduce a power, to make the scoring strictly
367 convex. Otherwise a symmetric knee beam (up/down/up/down)
368 does not have an optimum in the middle. */
370 ideal_score
= pow (ideal_score
, 1.1);
372 score
[d
] += length_pen
* ideal_score
;
379 score
[d
] /= max (count
[d
], 1);
380 while (flip (&d
) != DOWN
);
382 return score
[LEFT
] + score
[RIGHT
];
386 Beam::score_slopes_dy (Real yl
, Real yr
,
387 Real dy_mus
, Real dy_damp
,
391 Beam_quant_parameters
const *parameters
)
397 DAMPING_DIRECTION_PENALTY is a very harsh measure, while for
398 complex beaming patterns, horizontal is often a good choice.
400 TODO: find a way to incorporate the complexity of the beam in this
403 if (sign (dy_damp
) != sign (dy
))
407 if (fabs (dy_damp
/ dx
) > parameters
->ROUND_TO_ZERO_SLOPE
)
408 dem
+= parameters
->DAMPING_DIRECTION_PENALTY
;
410 dem
+= parameters
->HINT_DIRECTION_PENALTY
;
413 dem
+= parameters
->DAMPING_DIRECTION_PENALTY
;
416 dem
+= parameters
->MUSICAL_DIRECTION_FACTOR
417 * max (0.0, (fabs (dy
) - fabs (dy_mus
)));
419 Real slope_penalty
= parameters
->IDEAL_SLOPE_FACTOR
;
421 /* Xstaff beams tend to use extreme slopes to get short stems. We
422 put in a penalty here. */
426 /* Huh, why would a too steep beam be better than a too flat one ? */
427 dem
+= shrink_extra_weight (fabs (dy_damp
) - fabs (dy
), 1.5)
436 return x
- floor (x
);
440 TODO: The fixed value SECONDARY_BEAM_DEMERIT is probably flawed:
441 because for 32nd and 64th beams the forbidden quants are relatively
442 more important than stem lengths.
445 Beam::score_forbidden_quants (Real yl
, Real yr
,
448 Real thickness
, Real beam_translation
,
449 Drul_array
<int> beam_counts
,
450 Direction ldir
, Direction rdir
,
452 Beam_quant_parameters
const *parameters
)
455 Drul_array
<Real
> y (yl
, yr
);
456 Drul_array
<Direction
> dirs (ldir
, rdir
);
458 Real extra_demerit
= parameters
->SECONDARY_BEAM_DEMERIT
/ (max (beam_counts
[LEFT
], beam_counts
[RIGHT
]));
462 Real eps
= parameters
->BEAM_EPS
;
466 for (int j
= 1; j
<= beam_counts
[d
]; j
++)
468 Direction stem_dir
= dirs
[d
];
471 The 2.2 factor is to provide a little leniency for
472 borderline cases. If we do 2.0, then the upper outer line
473 will be in the gap of the (2, sit) quant, leading to a
476 Real gap1
= y
[d
] - stem_dir
* ((j
- 1) * beam_translation
+ thickness
/ 2 - slt
/ 2.2);
477 Real gap2
= y
[d
] - stem_dir
* (j
* beam_translation
- thickness
/ 2 + slt
/ 2.2);
480 gap
.add_point (gap1
);
481 gap
.add_point (gap2
);
483 for (Real k
= -radius
;
484 k
<= radius
+ eps
; k
+= 1.0)
485 if (gap
.contains (k
))
487 Real dist
= min (fabs (gap
[UP
] - k
), fabs (gap
[DOWN
] - k
));
490 this parameter is tuned to grace-stem-length.ly
492 Real fixed_demerit
= 0.4;
496 + (1 - fixed_demerit
) * (dist
/ gap
.length ()) * 2);
500 while ((flip (&d
)) != LEFT
);
502 if (max (beam_counts
[LEFT
], beam_counts
[RIGHT
]) >= 2)
505 Real sit
= (thickness
- slt
) / 2;
507 Real hang
= 1.0 - (thickness
- slt
) / 2;
512 if (beam_counts
[d
] >= 2
513 && fabs (y
[d
] - dirs
[d
] * beam_translation
) < radius
+ inter
)
515 if (dirs
[d
] == UP
&& dy
<= eps
516 && fabs (my_modf (y
[d
]) - sit
) < eps
)
517 dem
+= extra_demerit
;
519 if (dirs
[d
] == DOWN
&& dy
>= eps
520 && fabs (my_modf (y
[d
]) - hang
) < eps
)
521 dem
+= extra_demerit
;
524 if (beam_counts
[d
] >= 3
525 && fabs (y
[d
] - 2 * dirs
[d
] * beam_translation
) < radius
+ inter
)
527 if (dirs
[d
] == UP
&& dy
<= eps
528 && fabs (my_modf (y
[d
]) - straddle
) < eps
)
529 dem
+= extra_demerit
;
531 if (dirs
[d
] == DOWN
&& dy
>= eps
532 && fabs (my_modf (y
[d
]) - straddle
) < eps
)
533 dem
+= extra_demerit
;
536 while (flip (&d
) != LEFT
);