4 #include "staff-symbol-referencer.hh"
7 #include "paper-def.hh"
8 #include "group-interface.hh"
9 #include "align-interface.hh"
11 const int INTER_QUANT_PENALTY = 1000;
12 const int SECONDARY_BEAM_DEMERIT = 15;
13 const int STEM_LENGTH_DEMERIT_FACTOR = 5;
15 // possibly ridiculous, but too short stems just won't do
16 const int STEM_LENGTH_LIMIT_PENALTY = 5000;
17 const int DAMPING_DIRECTIION_PENALTY = 800;
18 const int MUSICAL_DIRECTION_FACTOR = 400;
19 const int IDEAL_SLOPE_FACTOR = 10;
23 shrink_extra_weight (Real x)
25 return fabs (x) * ((x < 0) ? 1.5 : 1.0);
40 - Make all demerits customisable
42 - One sensible check per demerit (what's this --hwn)
44 - Add demerits for quants per se, as to forbid a specific quant
49 int best_quant_score_idx (Array<Quant_score> const & qscores)
53 for (int i = qscores.size (); i--;)
55 if (qscores[i].demerits < best)
57 best = qscores [i].demerits ;
65 MAKE_SCHEME_CALLBACK (Beam, quanting, 1);
67 Beam::quanting (SCM smob)
69 Grob *me = unsmob_grob (smob);
71 SCM s = me->get_grob_property ("positions");
72 Real yl = gh_scm2double (gh_car (s));
73 Real yr = gh_scm2double (gh_cdr (s));
75 Real ss = Staff_symbol_referencer::staff_space (me);
76 Real thickness = gh_scm2double (me->get_grob_property ("thickness")) / ss;
77 Real slt = me->get_paper ()->get_var ("linethickness") / ss;
80 SCM sdy = me->get_grob_property ("least-squares-dy");
81 Real dy_mus = gh_number_p (sdy) ? gh_scm2double (sdy) : 0.0;
84 Real sit = (thickness - slt) / 2;
86 Real hang = 1.0 - (thickness - slt) / 2;
87 Real quants [] = {straddle, sit, inter, hang };
89 int num_quants = int (sizeof (quants)/sizeof (Real));
94 going to REGION_SIZE == 2, yields another 0.6 second with
98 (result indexes between 70 and 575) ? --hwn.
105 Do stem computations. These depend on YL and YR linearly, so we can
106 precompute for every stem 2 factors.
108 Link_array<Grob> stems=
109 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
110 Array<Stem_info> stem_infos;
111 Array<Real> base_lengths;
112 Array<Real> stem_xposns;
114 Drul_array<bool> dirs_found(0,0);
116 for (int a = 2; a--;)
117 common[a] = common_refpoint_of_array (stems, me, Axis(a));
119 Grob * fvs = first_visible_stem (me);
120 Grob *lvs = last_visible_stem (me);
121 Real xl = fvs ? fvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
122 Real xr = fvs ? lvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
125 We store some info to quickly interpolate.
127 Sometimes my head is screwed on backwards. The stemlength are
128 AFFINE linear in YL and YR. If YL == YR == 0, then we might have
129 stem_y != 0.0, when we're cross staff.
132 bool french = to_boolean (me->get_grob_property ("french-beaming"));
133 for (int i= 0; i < stems.size(); i++)
136 stem_infos.push (Stem::calc_stem_info (s));
137 dirs_found[stem_infos.top ().dir_] = true;
139 bool f = french && i > 0&& (i < stems.size () -1);
140 base_lengths.push (calc_stem_y (me, s, common, xl, xr,
142 stem_xposns.push (s->relative_coordinate (common[X_AXIS], X_AXIS));
148 Grob *commony = fvs->common_refpoint (lvs, Y_AXIS);
149 xstaff = Align_interface::has_interface (commony);
152 Direction ldir = Direction (stem_infos[0].dir_);
153 Direction rdir = Direction (stem_infos.top ().dir_);
154 bool knee_b = dirs_found[LEFT] && dirs_found[RIGHT];
157 int region_size = REGION_SIZE;
159 Knees are harder, lets try some more possibilities for knees.
164 for (int i = -region_size ; i < region_size; i++)
165 for (int j = 0; j < num_quants; j++)
167 quantsl.push (i + quants[j] + int (yl));
168 quantsr.push (i + quants[j] + int (yr));
171 Array<Quant_score> qscores;
173 for (int l =0; l < quantsl.size (); l++)
174 for (int r =0; r < quantsr.size (); r++)
185 This is a longish function, but we don't separate this out into
186 neat modular separate subfunctions, as the subfunctions would be
187 called for many values of YL, YR. By precomputing various
188 parameters outside of the loop, we can save a lot of time.
191 for (int i = qscores.size (); i--;)
194 += score_slopes_dy (qscores[i].yl, qscores[i].yr,
195 dy_mus, yr- yl, xstaff);
198 Real rad = Staff_symbol_referencer::staff_radius (me);
199 int beam_count = get_beam_count (me);
200 Real beam_translation = beam_count < 4
201 ? (2*ss + slt - thickness) / 2.0
202 : (3*ss + slt - thickness) / 3.0;
204 Real reasonable_score = (knee_b) ? 200000 : 100;
205 for (int i = qscores.size (); i--;)
206 if (qscores[i].demerits < reasonable_score)
209 += score_forbidden_quants (qscores[i].yl, qscores[i].yr,
210 rad, slt, thickness, beam_translation,
211 beam_count, ldir, rdir);
214 ; /* silly gdb thinks best_idx is inside for loop. */
215 for (int i = qscores.size (); i--;)
216 if (qscores[i].demerits < reasonable_score)
219 += score_stem_lengths (stems, stem_infos,
220 base_lengths, stem_xposns,
223 qscores[i].yl, qscores[i].yr);
226 ; /* silly gdb thinks best_idx is inside for loop. */
227 int best_idx = best_quant_score_idx (qscores);
228 me->set_grob_property ("positions",
229 gh_cons (gh_double2scm (qscores[best_idx].yl),
230 gh_double2scm (qscores[best_idx].yr))
236 me->set_grob_property ("quant-score",
237 gh_double2scm (qscores[best_idx].demerits));
238 me->set_grob_property ("best-idx", gh_int2scm (best_idx));
241 return SCM_UNSPECIFIED;
245 Beam::score_stem_lengths (Link_array<Grob>stems,
246 Array<Stem_info> stem_infos,
247 Array<Real> base_stem_ys,
253 Real pen = STEM_LENGTH_LIMIT_PENALTY;
255 Drul_array<Real> score (0, 0);
256 Drul_array<int> count (0, 0);
257 for (int i=0; i < stems.size (); i++)
260 if (Stem::invisible_b (s))
265 Real beam_y = yr *(x - xl)/dx + yl * ( xr - x)/dx;
266 Real current_y = beam_y + base_stem_ys[i];
268 Stem_info info = stem_infos[i];
269 Direction d = info.dir_;
272 * (0 >? (d * (info.shortest_y_ - current_y)));
274 Real ideal_score = shrink_extra_weight (d * current_y - d * info.ideal_y_);
278 we introduce a power, to make the scoring strictly
279 convex. Otherwise a symmetric knee beam (up/down/up/down) does
280 not have an optimum in the middle.
284 ideal_score = pow (ideal_score, 1.1);
285 score[d] += STEM_LENGTH_DEMERIT_FACTOR * ideal_score;
291 score[LEFT] /= count[LEFT];
293 score[RIGHT] /= count[RIGHT];
295 return score[LEFT]+score[RIGHT];
299 Beam::score_slopes_dy (Real yl, Real yr,
300 Real dy_mus, Real dy_damp,
306 if (sign (dy_damp) != sign (dy))
308 dem += DAMPING_DIRECTIION_PENALTY;
311 dem += MUSICAL_DIRECTION_FACTOR * (0 >? (fabs (dy) - fabs (dy_mus)));
314 Real slope_penalty = IDEAL_SLOPE_FACTOR;
317 Xstaff beams tend to use extreme slopes to get short stems. We
318 put in a penalty here.
323 dem += shrink_extra_weight (fabs (dy_damp) - fabs (dy))* slope_penalty;
330 return x - floor (x);
334 Beam::score_forbidden_quants (Real yl, Real yr,
337 Real thickness, Real beam_translation,
339 Direction ldir, Direction rdir)
344 if (fabs (yl) < rad && fabs ( my_modf (yl) - 0.5) < 1e-3)
345 dem += INTER_QUANT_PENALTY;
346 if (fabs (yr) < rad && fabs ( my_modf (yr) - 0.5) < 1e-3)
347 dem += INTER_QUANT_PENALTY;
349 // todo: use beam_count of outer stems.
354 Real sit = (thickness - slt) / 2;
356 Real hang = 1.0 - (thickness - slt) / 2;
359 if (fabs (yl - ldir * beam_translation) < rad
360 && fabs (my_modf (yl) - inter) < 1e-3)
361 dem += SECONDARY_BEAM_DEMERIT;
362 if (fabs (yr - rdir * beam_translation) < rad
363 && fabs (my_modf (yr) - inter) < 1e-3)
364 dem += SECONDARY_BEAM_DEMERIT;
369 Can't we simply compute the distance between the nearest
370 staffline and the secondary beam? That would get rid of the
371 silly case analysis here (which is probably not when we have
372 different beam-thicknesses.)
378 // hmm, without Interval/Drul_array, you get ~ 4x same code...
379 if (fabs (yl - ldir * beam_translation) < rad + inter)
381 if (ldir == UP && dy <= eps
382 && fabs (my_modf (yl) - sit) < eps)
383 dem += SECONDARY_BEAM_DEMERIT;
385 if (ldir == DOWN && dy >= eps
386 && fabs (my_modf (yl) - hang) < eps)
387 dem += SECONDARY_BEAM_DEMERIT;
390 if (fabs (yr - rdir * beam_translation) < rad + inter)
392 if (rdir == UP && dy >= eps
393 && fabs (my_modf (yr) - sit) < eps)
394 dem += SECONDARY_BEAM_DEMERIT;
396 if (rdir == DOWN && dy <= eps
397 && fabs (my_modf (yr) - hang) < eps)
398 dem += SECONDARY_BEAM_DEMERIT;
403 if (fabs (yl - 2 * ldir * beam_translation) < rad + inter)
405 if (ldir == UP && dy <= eps
406 && fabs (my_modf (yl) - straddle) < eps)
407 dem += SECONDARY_BEAM_DEMERIT;
409 if (ldir == DOWN && dy >= eps
410 && fabs (my_modf (yl) - straddle) < eps)
411 dem += SECONDARY_BEAM_DEMERIT;
414 if (fabs (yr - 2 * rdir * beam_translation) < rad + inter)
416 if (rdir == UP && dy >= eps
417 && fabs (my_modf (yr) - straddle) < eps)
418 dem += SECONDARY_BEAM_DEMERIT;
420 if (rdir == DOWN && dy <= eps
421 && fabs (my_modf (yr) - straddle) < eps)
422 dem += SECONDARY_BEAM_DEMERIT;