2 beam-quanting.cc -- implement Beam quanting functions
4 source file of the GNU LilyPond music typesetter
6 (c) 1997--2003 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7 Jan Nieuwenhuizen <janneke@gnu.org>
16 #include "staff-symbol-referencer.hh"
19 #include "paper-def.hh"
20 #include "group-interface.hh"
21 #include "align-interface.hh"
23 const int INTER_QUANT_PENALTY
= 1000;
24 const int SECONDARY_BEAM_DEMERIT
= 15;
25 const int STEM_LENGTH_DEMERIT_FACTOR
= 5;
27 // possibly ridiculous, but too short stems just won't do
28 const int STEM_LENGTH_LIMIT_PENALTY
= 5000;
29 const int DAMPING_DIRECTIION_PENALTY
= 800;
30 const int MUSICAL_DIRECTION_FACTOR
= 400;
31 const int IDEAL_SLOPE_FACTOR
= 10;
35 shrink_extra_weight (Real x
, Real fac
)
37 return fabs (x
) * ((x
< 0) ? fac
: 1.0);
52 - Make all demerits customisable
54 - One sensible check per demerit (what's this --hwn)
56 - Add demerits for quants per se, as to forbid a specific quant
61 int best_quant_score_idx (Array
<Quant_score
> const & qscores
)
65 for (int i
= qscores
.size (); i
--;)
67 if (qscores
[i
].demerits
< best
)
69 best
= qscores
[i
].demerits
;
77 MAKE_SCHEME_CALLBACK (Beam
, quanting
, 1);
79 Beam::quanting (SCM smob
)
81 Grob
*me
= unsmob_grob (smob
);
83 SCM s
= me
->get_grob_property ("positions");
84 Real yl
= gh_scm2double (gh_car (s
));
85 Real yr
= gh_scm2double (gh_cdr (s
));
87 Real ss
= Staff_symbol_referencer::staff_space (me
);
88 Real thickness
= gh_scm2double (me
->get_grob_property ("thickness")) / ss
;
89 Real slt
= me
->get_paper ()->get_realvar (ly_symbol2scm ("linethickness")) / ss
;
92 SCM sdy
= me
->get_grob_property ("least-squares-dy");
93 Real dy_mus
= gh_number_p (sdy
) ? gh_scm2double (sdy
) : 0.0;
96 Real sit
= (thickness
- slt
) / 2;
98 Real hang
= 1.0 - (thickness
- slt
) / 2;
99 Real quants
[] = {straddle
, sit
, inter
, hang
};
101 int num_quants
= int (sizeof (quants
)/sizeof (Real
));
106 going to REGION_SIZE == 2, yields another 0.6 second with
110 (result indexes between 70 and 575) ? --hwn.
117 Do stem computations. These depend on YL and YR linearly, so we can
118 precompute for every stem 2 factors.
120 Link_array
<Grob
> stems
=
121 Pointer_group_interface__extract_grobs (me
, (Grob
*)0, "stems");
122 Array
<Stem_info
> stem_infos
;
123 Array
<Real
> base_lengths
;
124 Array
<Real
> stem_xposns
;
126 Drul_array
<bool> dirs_found(0,0);
128 for (int a
= 2; a
--;)
129 common
[a
] = common_refpoint_of_array (stems
, me
, Axis(a
));
131 Grob
* fvs
= first_visible_stem (me
);
132 Grob
*lvs
= last_visible_stem (me
);
133 Real xl
= fvs
? fvs
->relative_coordinate (common
[X_AXIS
], X_AXIS
) : 0.0;
134 Real xr
= fvs
? lvs
->relative_coordinate (common
[X_AXIS
], X_AXIS
) : 0.0;
137 We store some info to quickly interpolate.
139 Sometimes my head is screwed on backwards. The stemlength are
140 AFFINE linear in YL and YR. If YL == YR == 0, then we might have
141 stem_y != 0.0, when we're cross staff.
144 for (int i
= 0; i
< stems
.size(); i
++)
147 stem_infos
.push (Stem::get_stem_info (s
));
148 dirs_found
[stem_infos
.top ().dir_
] = true;
150 bool f
= to_boolean (s
->get_grob_property ("french-beaming"))
151 && s
!= lvs
&& s
!= fvs
;
153 base_lengths
.push (calc_stem_y (me
, s
, common
, xl
, xr
,
155 stem_xposns
.push (s
->relative_coordinate (common
[X_AXIS
], X_AXIS
));
161 Grob
*commony
= fvs
->common_refpoint (lvs
, Y_AXIS
);
162 xstaff
= Align_interface::has_interface (commony
);
165 Direction ldir
= Direction (stem_infos
[0].dir_
);
166 Direction rdir
= Direction (stem_infos
.top ().dir_
);
167 bool knee_b
= dirs_found
[LEFT
] && dirs_found
[RIGHT
];
170 int region_size
= REGION_SIZE
;
172 Knees are harder, lets try some more possibilities for knees.
178 Asymetry ? should run to <= region_size ?
180 for (int i
= -region_size
; i
< region_size
; i
++)
181 for (int j
= 0; j
< num_quants
; j
++)
183 quantsl
.push (i
+ quants
[j
] + int (yl
));
184 quantsr
.push (i
+ quants
[j
] + int (yr
));
187 Array
<Quant_score
> qscores
;
189 for (int l
=0; l
< quantsl
.size (); l
++)
190 for (int r
=0; r
< quantsr
.size (); r
++)
200 /* This is a longish function, but we don't separate this out into
201 neat modular separate subfunctions, as the subfunctions would be
202 called for many values of YL, YR. By precomputing various
203 parameters outside of the loop, we can save a lot of time. */
204 for (int i
= qscores
.size (); i
--;)
207 += score_slopes_dy (qscores
[i
].yl
, qscores
[i
].yr
,
208 dy_mus
, yr
- yl
, xstaff
);
211 Real rad
= Staff_symbol_referencer::staff_radius (me
);
212 int beam_count
= get_beam_count (me
);
213 Real beam_translation
= get_beam_translation (me
);
215 Real reasonable_score
= (knee_b
) ? 200000 : 100;
216 for (int i
= qscores
.size (); i
--;)
217 if (qscores
[i
].demerits
< reasonable_score
)
220 += score_forbidden_quants (qscores
[i
].yl
, qscores
[i
].yr
,
221 rad
, slt
, thickness
, beam_translation
,
222 beam_count
, ldir
, rdir
);
225 // ; /* silly gdb thinks best_idx is inside for loop. */
226 for (int i
= qscores
.size (); i
--;)
227 if (qscores
[i
].demerits
< reasonable_score
)
230 += score_stem_lengths (stems
, stem_infos
,
231 base_lengths
, stem_xposns
,
234 qscores
[i
].yl
, qscores
[i
].yr
);
237 // ; /* silly gdb thinks best_idx is inside for loop. */
238 int best_idx
= best_quant_score_idx (qscores
);
239 me
->set_grob_property ("positions",
240 gh_cons (gh_double2scm (qscores
[best_idx
].yl
),
241 gh_double2scm (qscores
[best_idx
].yr
))
247 me
->set_grob_property ("quant-score",
248 gh_double2scm (qscores
[best_idx
].demerits
));
249 me
->set_grob_property ("best-idx", scm_int2num (best_idx
));
252 return SCM_UNSPECIFIED
;
256 Beam::score_stem_lengths (Link_array
<Grob
> const &stems
,
257 Array
<Stem_info
> const &stem_infos
,
258 Array
<Real
> const &base_stem_ys
,
259 Array
<Real
> const &stem_xs
,
264 Real limit_penalty
= STEM_LENGTH_LIMIT_PENALTY
;
265 Drul_array
<Real
> score (0, 0);
266 Drul_array
<int> count (0, 0);
268 for (int i
=0; i
< stems
.size (); i
++)
271 if (Stem::invisible_b (s
))
276 Real beam_y
= dx
? yr
*(x
- xl
)/dx
+ yl
* ( xr
- x
)/dx
: (yr
+ yl
)/2;
277 Real current_y
= beam_y
+ base_stem_ys
[i
];
278 Real length_pen
= STEM_LENGTH_DEMERIT_FACTOR
;
280 Stem_info info
= stem_infos
[i
];
281 Direction d
= info
.dir_
;
283 score
[d
] += limit_penalty
* (0 >? (d
* (info
.shortest_y_
- current_y
)));
285 Real ideal_diff
= d
* (current_y
- info
.ideal_y_
);
286 Real ideal_score
= shrink_extra_weight (ideal_diff
, 1.5);
288 /* We introduce a power, to make the scoring strictly
289 convex. Otherwise a symmetric knee beam (up/down/up/down)
290 does not have an optimum in the middle. */
292 ideal_score
= pow (ideal_score
, 1.1);
294 score
[d
] += length_pen
* ideal_score
;
302 score
[d
] /= (count
[d
] >? 1);
304 while (flip (&d
) != DOWN
);
306 return score
[LEFT
]+score
[RIGHT
];
310 Beam::score_slopes_dy (Real yl
, Real yr
,
311 Real dy_mus
, Real dy_damp
,
317 if (sign (dy_damp
) != sign (dy
))
319 dem
+= DAMPING_DIRECTIION_PENALTY
;
322 dem
+= MUSICAL_DIRECTION_FACTOR
* (0 >? (fabs (dy
) - fabs (dy_mus
)));
325 Real slope_penalty
= IDEAL_SLOPE_FACTOR
;
327 /* Xstaff beams tend to use extreme slopes to get short stems. We
328 put in a penalty here. */
332 /* Huh, why would a too steep beam be better than a too flat one ? */
333 dem
+= shrink_extra_weight (fabs (dy_damp
) - fabs (dy
), 1.5)
341 return x
- floor (x
);
345 Beam::score_forbidden_quants (Real yl
, Real yr
,
348 Real thickness
, Real beam_translation
,
350 Direction ldir
, Direction rdir
)
355 for (int i
= 0; i
< 2; i
++)
358 if (fabs (y
) <= (radius
+ 0.5) && fabs ( my_modf (y
) - 0.5) < 1e-3)
359 dem
+= INTER_QUANT_PENALTY
;
362 // todo: use beam_count of outer stems.
366 Real sit
= (thickness
- slt
) / 2;
368 Real hang
= 1.0 - (thickness
- slt
) / 2;
371 if (fabs (yl
- ldir
* beam_translation
) < radius
372 && fabs (my_modf (yl
) - inter
) < 1e-3)
373 dem
+= SECONDARY_BEAM_DEMERIT
;
374 if (fabs (yr
- rdir
* beam_translation
) < radius
375 && fabs (my_modf (yr
) - inter
) < 1e-3)
376 dem
+= SECONDARY_BEAM_DEMERIT
;
381 Can't we simply compute the distance between the nearest
382 staffline and the secondary beam? That would get rid of the
383 silly case analysis here (which is probably not valid when we
384 have different beam-thicknesses.)
390 // hmm, without Interval/Drul_array, you get ~ 4x same code...
391 if (fabs (yl
- ldir
* beam_translation
) < radius
+ inter
)
393 if (ldir
== UP
&& dy
<= eps
394 && fabs (my_modf (yl
) - sit
) < eps
)
395 dem
+= SECONDARY_BEAM_DEMERIT
;
397 if (ldir
== DOWN
&& dy
>= eps
398 && fabs (my_modf (yl
) - hang
) < eps
)
399 dem
+= SECONDARY_BEAM_DEMERIT
;
402 if (fabs (yr
- rdir
* beam_translation
) < radius
+ inter
)
404 if (rdir
== UP
&& dy
>= eps
405 && fabs (my_modf (yr
) - sit
) < eps
)
406 dem
+= SECONDARY_BEAM_DEMERIT
;
408 if (rdir
== DOWN
&& dy
<= eps
409 && fabs (my_modf (yr
) - hang
) < eps
)
410 dem
+= SECONDARY_BEAM_DEMERIT
;
415 if (fabs (yl
- 2 * ldir
* beam_translation
) < radius
+ inter
)
417 if (ldir
== UP
&& dy
<= eps
418 && fabs (my_modf (yl
) - straddle
) < eps
)
419 dem
+= SECONDARY_BEAM_DEMERIT
;
421 if (ldir
== DOWN
&& dy
>= eps
422 && fabs (my_modf (yl
) - straddle
) < eps
)
423 dem
+= SECONDARY_BEAM_DEMERIT
;
426 if (fabs (yr
- 2 * rdir
* beam_translation
) < radius
+ inter
)
428 if (rdir
== UP
&& dy
>= eps
429 && fabs (my_modf (yr
) - straddle
) < eps
)
430 dem
+= SECONDARY_BEAM_DEMERIT
;
432 if (rdir
== DOWN
&& dy
<= eps
433 && fabs (my_modf (yr
) - straddle
) < eps
)
434 dem
+= SECONDARY_BEAM_DEMERIT
;