2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 1997--2011 Han-Wen Nienhuys <hanwen@xs4all.nl>
5 Jan Nieuwenhuizen <janneke@gnu.org>
7 LilyPond is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
12 LilyPond is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with LilyPond. If not, see <http://www.gnu.org/licenses/>.
21 #include "beam-scoring-problem.hh"
28 #include "align-interface.hh"
30 #include "direction.hh"
31 #include "directional-element-interface.hh"
33 #include "grob-array.hh"
35 #include "international.hh"
36 #include "least-squares.hh"
37 #include "libc-extension.hh"
39 #include "note-head.hh"
40 #include "output-def.hh"
41 #include "pointer-group-interface.hh"
42 #include "rhythmic-head.hh"
43 #include "staff-symbol-referencer.hh"
49 get_detail (SCM alist, SCM sym, Real def)
51 SCM entry = scm_assq (sym, alist);
53 if (scm_is_pair (entry))
54 return robust_scm2double (scm_cdr (entry), def);
59 Beam_quant_parameters::fill (Grob *him)
61 SCM details = him->get_property ("details");
64 BEAM_EPS = get_detail (details, ly_symbol2scm ("beam-eps"), 1e-3);
65 REGION_SIZE = get_detail (details, ly_symbol2scm ("region-size"), 2);
68 SECONDARY_BEAM_DEMERIT = get_detail (details, ly_symbol2scm ("secondary-beam-demerit"), 10.0);
69 STEM_LENGTH_DEMERIT_FACTOR = get_detail (details, ly_symbol2scm ("stem-length-demerit-factor"), 5);
70 HORIZONTAL_INTER_QUANT_PENALTY = get_detail (details, ly_symbol2scm ("horizontal-inter-quant"), 500);
72 STEM_LENGTH_LIMIT_PENALTY = get_detail (details, ly_symbol2scm ("stem-length-limit-penalty"), 5000);
73 DAMPING_DIRECTION_PENALTY = get_detail (details, ly_symbol2scm ("damping-direction-penalty"), 800);
74 HINT_DIRECTION_PENALTY = get_detail (details, ly_symbol2scm ("hint-direction-penalty"), 20);
75 MUSICAL_DIRECTION_FACTOR = get_detail (details, ly_symbol2scm ("musical-direction-factor"), 400);
76 IDEAL_SLOPE_FACTOR = get_detail (details, ly_symbol2scm ("ideal-slope-factor"), 10);
77 ROUND_TO_ZERO_SLOPE = get_detail (details, ly_symbol2scm ("round-to-zero-slope"), 0.02);
80 COLLISION_PENALTY = get_detail (details, ly_symbol2scm ("collision-penalty"), 500);
81 COLLISION_PADDING = get_detail (details, ly_symbol2scm ("collision-padding"), 0.5);
82 STEM_COLLISION_FACTOR = get_detail (details, ly_symbol2scm ("stem-collision-factor"), 0.1);
85 // Add x if x is positive, add |x|*fac if x is negative.
87 shrink_extra_weight (Real x, Real fac)
89 return fabs (x) * ((x < 0) ? fac : 1.0);
92 /****************************************************************/
94 Beam_configuration::Beam_configuration ()
96 y = Interval (0.0, 0.0);
98 next_scorer_todo = ORIGINAL_DISTANCE;
101 bool Beam_configuration::done () const
103 return next_scorer_todo >= NUM_SCORERS;
106 void Beam_configuration::add (Real demerit, const string &reason)
110 #if DEBUG_BEAM_SCORING
112 score_card_ += to_string (" %s %.2f", reason.c_str (), demerit);
116 Beam_configuration *Beam_configuration::new_config (Interval start,
119 Beam_configuration *qs = new Beam_configuration;
120 qs->y = Interval (int (start[LEFT]) + offset[LEFT],
121 int (start[RIGHT]) + offset[RIGHT]);
123 // This orders the sequence so we try combinations closest to the
124 // the ideal offset first.
125 Real start_score = abs (offset[RIGHT]) + abs (offset[LEFT]);
126 qs->demerits = start_score / 1000.0;
127 qs->next_scorer_todo = ORIGINAL_DISTANCE + 1;
133 Beam_scoring_problem::y_at (Real x, Beam_configuration const *p) const
135 return p->y[LEFT] + (x - x_span_[LEFT]) * p->y.delta () / x_span_.delta ();
138 /****************************************************************/
143 - Make all demerits customisable
145 - Add demerits for quants per se, as to forbid a specific quant
149 // This is a temporary hack to see how much we can gain by using a
150 // priority queue on the beams to score.
151 static int score_count = 0;
152 LY_DEFINE (ly_beam_score_count, "ly:beam-score-count", 0, 0, 0,
154 "count number of beam scores.")
156 return scm_from_int (score_count);
159 void Beam_scoring_problem::add_collision (Real x, Interval y,
162 if (edge_dirs_[LEFT] == edge_dirs_[RIGHT])
164 Direction d = edge_dirs_[LEFT];
166 Real quant_range_y = quant_range_[LEFT][-d]
167 + (x - x_span_[LEFT]) * (quant_range_[RIGHT][-d] - quant_range_[LEFT][-d]) / x_span_.delta ();
169 if (d * (quant_range_y - minmax (d, y[UP], y[DOWN])) > 0)
176 c.beam_y_.set_empty ();
178 for (vsize j = 0; j < segments_.size (); j++)
180 if (segments_[j].horizontal_.contains (x))
181 c.beam_y_.add_point (segments_[j].vertical_count_ * beam_translation_);
182 if (segments_[j].horizontal_[LEFT] > x)
185 c.beam_y_.widen (0.5 * beam_thickness_);
189 y *= 1 / staff_space_;
191 c.base_penalty_ = score_factor;
192 collisions_.push_back (c);
195 void Beam_scoring_problem::init_collisions (vector<Grob *> grobs)
197 Grob *common_x = NULL;
198 segments_ = Beam::get_beam_segments (beam_, &common_x);
199 vector_sort (segments_, beam_segment_less);
200 if (common_[X_AXIS] != common_x)
202 programming_error ("Disagree on common x. Skipping collisions in beam scoring.");
207 for (vsize i = 0; i < grobs.size (); i++)
210 for (Axis a = X_AXIS; a < NO_AXES; incr (a))
211 b[a] = grobs[i]->extent (common_[a], a);
213 Real width = b[X_AXIS].length ();
214 Real width_factor = sqrt (width / staff_space_);
218 add_collision (b[X_AXIS][d], b[Y_AXIS], width_factor);
219 while (flip (&d) != LEFT);
221 Grob *stem = unsmob_grob (grobs[i]->get_object ("stem"));
222 if (stem && Stem::has_interface (stem) && Stem::is_normal_stem (stem))
228 for (set<Grob *>::const_iterator it (stems.begin ()); it != stems.end (); it++)
231 Real x = s->extent (common_[X_AXIS], X_AXIS).center ();
233 Direction stem_dir = get_grob_direction (*it);
236 y[-stem_dir] = Stem::chord_start_y (*it) + (*it)->relative_coordinate (common_[Y_AXIS], Y_AXIS)
237 - beam_->relative_coordinate (common_[Y_AXIS], Y_AXIS);
239 Real factor = parameters_.STEM_COLLISION_FACTOR;
240 if (!unsmob_grob (s->get_object ("beam")))
242 add_collision (x, y, factor);
246 void Beam_scoring_problem::init_stems ()
248 extract_grob_set (beam_, "covered-grobs", collisions);
249 extract_grob_set (beam_, "stems", stems);
250 for (int a = 2; a--;)
252 common_[a] = common_refpoint_of_array (stems, beam_, Axis (a));
253 common_[a] = common_refpoint_of_array (collisions, common_[a], Axis (a));
256 Drul_array<Grob *> edge_stems (Beam::first_normal_stem (beam_),
257 Beam::last_normal_stem (beam_));
260 x_span_[d] = edge_stems[d] ? edge_stems[d]->relative_coordinate (common_[X_AXIS], X_AXIS) : 0.0;
261 while (flip (&d) != LEFT);
263 Drul_array<bool> dirs_found (0, 0);
264 for (vsize i = 0; i < stems.size (); i++)
267 if (!Stem::is_normal_stem (s))
270 Stem_info si (Stem::get_stem_info (s));
271 si.scale (1 / staff_space_);
272 stem_infos_.push_back (si);
273 dirs_found[si.dir_] = true;
275 bool f = to_boolean (s->get_property ("french-beaming"))
276 && s != edge_stems[LEFT] && s != edge_stems[RIGHT];
278 Real y = Beam::calc_stem_y (beam_, s, common_, x_span_[LEFT], x_span_[RIGHT], CENTER,
280 base_lengths_.push_back (y / staff_space_);
281 stem_xpositions_.push_back (s->relative_coordinate (common_[X_AXIS], X_AXIS));
284 edge_dirs_ = Drul_array<Direction> (CENTER, CENTER);
285 if (stem_infos_.size ())
287 edge_dirs_ = Drul_array<Direction> (stem_infos_[0].dir_,
288 stem_infos_.back ().dir_);
291 is_xstaff_ = Align_interface::has_interface (common_[Y_AXIS]);
292 is_knee_ = dirs_found[LEFT] && dirs_found[RIGHT];
294 staff_radius_ = Staff_symbol_referencer::staff_radius (beam_);
295 edge_beam_counts_ = Drul_array<int>
296 (Stem::beam_multiplicity (stems[0]).length () + 1,
297 Stem::beam_multiplicity (stems.back ()).length () + 1);
299 // TODO - why are we dividing by staff_space_?
300 beam_translation_ = Beam::get_beam_translation (beam_) / staff_space_;
305 quant_range_[d].set_full ();
309 Real stem_offset = edge_stems[d]->relative_coordinate (common_[Y_AXIS], Y_AXIS)
310 - beam_->relative_coordinate (common_[Y_AXIS], Y_AXIS);
311 Interval heads = Stem::head_positions (edge_stems[d]) * 0.5 * staff_space_;
313 Direction ed = edge_dirs_[d];
314 heads.widen (0.5 * staff_space_
315 + (edge_beam_counts_[d] - 1) * beam_translation_ + beam_thickness_ * .5);
316 quant_range_[d][-ed] = heads[ed] + stem_offset;
318 while (flip (&d) != LEFT);
320 init_collisions (collisions);
323 Beam_scoring_problem::Beam_scoring_problem (Grob *me, Drul_array<Real> ys)
329 Calculations are relative to a unit-scaled staff, i.e. the quants are
330 divided by the current staff_space_.
332 staff_space_ = Staff_symbol_referencer::staff_space (me);
333 beam_thickness_ = Beam::get_beam_thickness (me) / staff_space_;
334 line_thickness_ = Staff_symbol_referencer::line_thickness (me) / staff_space_;
336 // This is the least-squares DY, corrected for concave beams.
337 musical_dy_ = robust_scm2double (me->get_property ("least-squares-dy"), 0);
339 parameters_.fill (me);
343 // Assuming V is not empty, pick a 'reasonable' point inside V.
345 point_in_interval (Interval v, Real dist)
349 else if (isinf (v[UP]))
350 return v[DOWN] + dist;
355 /* Set stem's shorten property if unset.
358 take some y-position (chord/beam/nearest?) into account
359 scmify forced-fraction
361 This is done in beam because the shorten has to be uniform over the
366 set_minimum_dy (Grob *me, Real *dy)
371 If dy is smaller than the smallest quant, we
372 get absurd direction-sign penalties.
375 Real ss = Staff_symbol_referencer::staff_space (me);
376 Real beam_thickness = Beam::get_beam_thickness (me) / ss;
377 Real slt = Staff_symbol_referencer::line_thickness (me) / ss;
378 Real sit = (beam_thickness - slt) / 2;
380 Real hang = 1.0 - (beam_thickness - slt) / 2;
382 *dy = sign (*dy) * max (fabs (*dy),
383 min (min (sit, inter), hang));
388 Beam::no_visible_stem_positions (Grob *me, Interval default_value)
390 extract_grob_set (me, "stems", stems);
392 return default_value;
394 Interval head_positions;
396 for (vsize i = 0; i < stems.size (); i++)
398 head_positions.unite (Stem::head_positions (stems[i]));
399 multiplicity.unite (Stem::beam_multiplicity (stems[i]));
402 Direction dir = get_grob_direction (me);
405 programming_error ("The beam should have a direction by now.");
407 Real y = head_positions.linear_combination (dir)
408 * 0.5 * Staff_symbol_referencer::staff_space (me)
409 + dir * get_beam_translation (me) * (multiplicity.length () + 1);
411 y /= Staff_symbol_referencer::staff_space (me);
412 return Interval (y, y);
416 Compute a first approximation to the beam slope.
418 MAKE_SCHEME_CALLBACK (Beam, calc_least_squares_positions, 2);
420 Beam::calc_least_squares_positions (SCM smob, SCM /* posns */)
422 Grob *me = unsmob_grob (smob);
424 int count = normal_stem_count (me);
427 return ly_interval2scm (no_visible_stem_positions (me, pos));
429 vector<Real> x_posns;
430 extract_grob_set (me, "normal-stems", stems);
431 Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
432 Grob *commony = common_refpoint_of_array (stems, me, Y_AXIS);
434 Real my_y = me->relative_coordinate (commony, Y_AXIS);
436 Grob *fvs = first_normal_stem (me);
437 Grob *lvs = last_normal_stem (me);
439 Interval ideal (Stem::get_stem_info (fvs).ideal_y_
440 + fvs->relative_coordinate (commony, Y_AXIS) - my_y,
441 Stem::get_stem_info (lvs).ideal_y_
442 + lvs->relative_coordinate (commony, Y_AXIS) - my_y);
444 Real x0 = first_normal_stem (me)->relative_coordinate (commonx, X_AXIS);
445 for (vsize i = 0; i < stems.size (); i++)
449 Real x = s->relative_coordinate (commonx, X_AXIS) - x0;
450 x_posns.push_back (x);
452 Real dx = last_normal_stem (me)->relative_coordinate (commonx, X_AXIS) - x0;
460 Interval chord (Stem::chord_start_y (stems[0]),
461 Stem::chord_start_y (stems.back ()));
463 /* Simple beams (2 stems) on middle line should be allowed to be
466 However, if both stems reach middle line,
467 ideal[LEFT] == ideal[RIGHT] and ideal.delta () == 0.
469 For that case, we apply artificial slope */
470 if (!ideal[LEFT] && chord.delta () && count == 2)
473 Direction d = (Direction) (sign (chord.delta ()) * UP);
474 pos[d] = get_beam_thickness (me) / 2;
481 For broken beams this doesn't work well. In this case, the
482 slope esp. of the first part of a broken beam should predict
483 where the second part goes.
485 ldy = pos[RIGHT] - pos[LEFT];
489 vector<Offset> ideals;
490 for (vsize i = 0; i < stems.size (); i++)
493 ideals.push_back (Offset (x_posns[i],
494 Stem::get_stem_info (s).ideal_y_
495 + s->relative_coordinate (commony, Y_AXIS)
499 minimise_least_squares (&slope, &y, ideals);
503 set_minimum_dy (me, &dy);
506 pos = Interval (y, (y + dy));
510 "position" is relative to the staff.
512 scale_drul (&pos, 1 / Staff_symbol_referencer::staff_space (me));
514 me->set_property ("least-squares-dy", scm_from_double (ldy));
515 return ly_interval2scm (pos);
518 /* This neat trick is by Werner Lemberg,
519 damped = tanh (slope)
520 corresponds with some tables in [Wanske] CHECKME */
521 MAKE_SCHEME_CALLBACK (Beam, slope_damping, 2);
523 Beam::slope_damping (SCM smob, SCM posns)
525 Grob *me = unsmob_grob (smob);
526 Drul_array<Real> pos = ly_scm2interval (posns);
528 if (normal_stem_count (me) <= 1)
531 SCM s = me->get_property ("damping");
532 Real damping = scm_to_double (s);
533 Real concaveness = robust_scm2double (me->get_property ("concaveness"), 0.0);
534 if (concaveness >= 10000)
536 pos[LEFT] = pos[RIGHT];
537 me->set_property ("least-squares-dy", scm_from_double (0));
543 scale_drul (&pos, Staff_symbol_referencer::staff_space (me));
545 Real dy = pos[RIGHT] - pos[LEFT];
547 Grob *fvs = first_normal_stem (me);
548 Grob *lvs = last_normal_stem (me);
550 Grob *commonx = fvs->common_refpoint (lvs, X_AXIS);
552 Real dx = last_normal_stem (me)->relative_coordinate (commonx, X_AXIS)
553 - first_normal_stem (me)->relative_coordinate (commonx, X_AXIS);
555 Real slope = dy && dx ? dy / dx : 0;
557 slope = 0.6 * tanh (slope) / (damping + concaveness);
559 Real damped_dy = slope * dx;
561 set_minimum_dy (me, &damped_dy);
563 pos[LEFT] += (dy - damped_dy) / 2;
564 pos[RIGHT] -= (dy - damped_dy) / 2;
566 scale_drul (&pos, 1 / Staff_symbol_referencer::staff_space (me));
569 return ly_interval2scm (pos);
573 We can't combine with previous function, since check concave and
574 slope damping comes first.
576 TODO: we should use the concaveness to control the amount of damping
579 MAKE_SCHEME_CALLBACK (Beam, shift_region_to_valid, 2);
581 Beam::shift_region_to_valid (SCM grob, SCM posns)
583 Grob *me = unsmob_grob (grob);
588 vector<Real> x_posns;
589 extract_grob_set (me, "stems", stems);
590 extract_grob_set (me, "covered-grobs", covered);
592 Grob *common[NO_AXES] = { me, me };
593 for (Axis a = X_AXIS; a < NO_AXES; incr (a))
595 common[a] = common_refpoint_of_array (stems, me, a);
596 common[a] = common_refpoint_of_array (covered, common[a], a);
598 Grob *fvs = first_normal_stem (me);
603 x_span[LEFT] = fvs->relative_coordinate (common[X_AXIS], X_AXIS);
604 for (vsize i = 0; i < stems.size (); i++)
608 Real x = s->relative_coordinate (common[X_AXIS], X_AXIS) - x_span[LEFT];
609 x_posns.push_back (x);
612 Grob *lvs = last_normal_stem (me);
613 x_span[RIGHT] = lvs->relative_coordinate (common[X_AXIS], X_AXIS);
615 Drul_array<Real> pos = ly_scm2interval (posns);
617 scale_drul (&pos, Staff_symbol_referencer::staff_space (me));
619 Real beam_dy = pos[RIGHT] - pos[LEFT];
620 Real beam_left_y = pos[LEFT];
621 Real slope = x_span.delta () ? (beam_dy / x_span.delta ()) : 0.0;
624 Shift the positions so that we have a chance of finding good
625 quants (i.e. no short stem failures.)
627 Interval feasible_left_point;
628 feasible_left_point.set_full ();
630 for (vsize i = 0; i < stems.size (); i++)
633 if (Stem::is_invisible (s))
636 Direction d = get_grob_direction (s);
638 = Stem::get_stem_info (s).shortest_y_
639 - slope * x_posns [i];
642 left_y is now relative to the stem S. We want relative to
643 ourselves, so translate:
646 += + s->relative_coordinate (common[Y_AXIS], Y_AXIS)
647 - me->relative_coordinate (common[Y_AXIS], Y_AXIS);
653 feasible_left_point.intersect (flp);
656 vector<Grob *> filtered;
658 We only update these for objects that are too large for quanting
659 to find a workaround. Typically, these are notes with
660 stems, and timesig/keysig/clef, which take out the entire area
661 inside the staff as feasible.
663 The code below disregards the thickness and multiplicity of the
664 beam. This should not be a problem, as the beam quanting will
665 take care of computing the impact those exactly.
667 Real min_y_size = 2.0;
669 // A list of intervals into which beams may not fall
670 vector<Interval> forbidden_intervals;
672 for (vsize i = 0; i < covered.size (); i++)
674 if (!covered[i]->is_live ())
677 if (Beam::has_interface (covered[i]) && is_cross_staff (covered[i]))
681 for (Axis a = X_AXIS; a < NO_AXES; incr (a))
682 b[a] = covered[i]->extent (common[a], a);
684 if (b[X_AXIS].is_empty () || b[Y_AXIS].is_empty ())
687 if (intersection (b[X_AXIS], x_span).is_empty ())
690 filtered.push_back (covered[i]);
691 Grob *head_stem = Rhythmic_head::get_stem (covered[i]);
692 if (head_stem && Stem::is_normal_stem (head_stem)
693 && Note_head::has_interface (covered[i]))
695 if (Stem::get_beam (head_stem))
698 We must assume that stems are infinitely long in this
699 case, as asking for the length of the stem typically
700 leads to circular dependencies.
702 This strategy assumes that we don't want to handle the
703 collision of beams in opposite non-forced directions
704 with this code, where shortening the stems of both
705 would resolve the problem, eg.
715 Such beams would need a coordinating grob to resolve
716 the collision, since both will likely want to occupy
719 Direction stemdir = get_grob_direction (head_stem);
720 b[Y_AXIS][stemdir] = stemdir * infinity_f;
724 // TODO - should we include the extent of the stem here?
728 if (b[Y_AXIS].length () < min_y_size)
734 Real x = b[X_AXIS][d] - x_span[LEFT];
741 Real left_y = b[Y_AXIS][yd];
745 // Translate back to beam as ref point.
746 left_y -= me->relative_coordinate (common[Y_AXIS], Y_AXIS);
748 disallowed[yd] = left_y;
750 while (flip (&yd) != DOWN);
752 forbidden_intervals.push_back (disallowed);
754 while (flip (&d) != LEFT);
758 = Pointer_group_interface::get_grob_array (me,
759 ly_symbol2scm ("covered-grobs"));
760 arr->set_array (filtered);
762 vector_sort (forbidden_intervals, Interval::left_less);
763 Real epsilon = 1.0e-10;
764 Interval feasible_beam_placements (beam_left_y, beam_left_y);
767 forbidden_intervals contains a vector of intervals in which
768 the beam cannot start. it iterates through these intervals,
769 pushing feasible_beam_placements epsilon over or epsilon under a
770 collision. when this type of change happens, the loop is marked
771 as "dirty" and re-iterated.
773 TODO: figure out a faster ways that this loop can happen via
774 a better search algorithm and/or OOP.
781 for (vsize i = 0; i < forbidden_intervals.size (); i++)
786 if (forbidden_intervals[i][d] == d * infinity_f)
787 feasible_beam_placements[d] = d * infinity_f;
788 else if (forbidden_intervals[i].contains (feasible_beam_placements[d]))
790 feasible_beam_placements[d] = d * epsilon + forbidden_intervals[i][d];
794 while (flip (&d) != DOWN);
799 // if the beam placement falls out of the feasible region, we push it
800 // to infinity so that it can never be a feasible candidate below
804 if (!feasible_left_point.contains (feasible_beam_placements[d]))
805 feasible_beam_placements[d] = d * infinity_f;
807 while (flip (&d) != DOWN);
809 if ((feasible_beam_placements[UP] == infinity_f && feasible_beam_placements[DOWN] == -infinity_f) && !feasible_left_point.is_empty ())
811 // We are somewhat screwed: we have a collision, but at least
812 // there is a way to satisfy stem length constraints.
813 beam_left_y = point_in_interval (feasible_left_point, 2.0);
815 else if (!feasible_left_point.is_empty ())
817 // Only one of them offers is feasible solution. Pick that one.
818 if (abs (beam_left_y - feasible_beam_placements[DOWN]) > abs (beam_left_y - feasible_beam_placements[UP]))
819 beam_left_y = feasible_beam_placements[UP];
821 beam_left_y = feasible_beam_placements[DOWN];
825 // We are completely screwed.
826 me->warning (_ ("no viable initial configuration found: may not find good beam slope"));
829 pos = Drul_array<Real> (beam_left_y, (beam_left_y + beam_dy));
830 scale_drul (&pos, 1 / Staff_symbol_referencer::staff_space (me));
832 return ly_interval2scm (pos);
836 Beam_scoring_problem::generate_quants (vector<Beam_configuration *> *scores) const
838 int region_size = (int) parameters_.REGION_SIZE;
840 // Knees and collisions are harder, lets try some more possibilities
843 if (collisions_.size ())
847 Real sit = (beam_thickness_ - line_thickness_) / 2;
849 Real hang = 1.0 - (beam_thickness_ - line_thickness_) / 2;
850 Real base_quants [] = {straddle, sit, inter, hang};
851 int num_base_quants = int (sizeof (base_quants) / sizeof (Real));
854 Asymetry ? should run to <= region_size ?
856 vector<Real> unshifted_quants;
857 for (int i = -region_size; i < region_size; i++)
858 for (int j = 0; j < num_base_quants; j++)
860 unshifted_quants.push_back (i + base_quants[j]);
863 for (vsize i = 0; i < unshifted_quants.size (); i++)
864 for (vsize j = 0; j < unshifted_quants.size (); j++)
866 Beam_configuration *c
867 = Beam_configuration::new_config (unquanted_y_,
868 Interval (unshifted_quants[i],
869 unshifted_quants[j]));
874 if (!quant_range_[d].contains (c->y[d]))
881 while (flip (&d) != LEFT);
883 scores->push_back (c);
888 void Beam_scoring_problem::one_scorer (Beam_configuration *config) const
891 switch (config->next_scorer_todo)
894 score_slope_ideal (config);
896 case SLOPE_DIRECTION:
897 score_slope_direction (config);
900 score_slope_musical (config);
903 score_forbidden_quants (config);
906 score_stem_lengths (config);
909 score_collisions (config);
911 case HORIZONTAL_INTER:
912 score_horizontal_inter_quants (config);
916 case ORIGINAL_DISTANCE:
920 config->next_scorer_todo++;
924 Beam_scoring_problem::force_score (SCM inspect_quants, const vector<Beam_configuration *> &configs) const
926 Drul_array<Real> ins = ly_scm2interval (inspect_quants);
928 Beam_configuration *best = NULL;
929 for (vsize i = 0; i < configs.size (); i++)
931 Real d = fabs (configs[i]->y[LEFT] - ins[LEFT]) + fabs (configs[i]->y[RIGHT] - ins[RIGHT]);
939 programming_error ("cannot find quant");
941 while (!best->done ())
948 Beam_scoring_problem::solve () const
950 vector<Beam_configuration *> configs;
951 generate_quants (&configs);
953 if (configs.empty ())
955 programming_error ("No viable beam quanting found. Using unquanted y value.");
959 Beam_configuration *best = NULL;
962 = to_boolean (beam_->layout ()->lookup_variable (ly_symbol2scm ("debug-beam-scoring")));
963 SCM inspect_quants = beam_->get_property ("inspect-quants");
964 if (scm_is_pair (inspect_quants))
967 best = force_score (inspect_quants, configs);
971 std::priority_queue < Beam_configuration *, std::vector<Beam_configuration *>,
972 Beam_configuration_less > queue;
973 for (vsize i = 0; i < configs.size (); i++)
974 queue.push (configs[i]);
979 It would be neat if we generated new configurations on the
980 fly, depending on the best complete score so far, eg.
983 if (best->demerits < sqrt(queue.size())
985 while (best->demerits > sqrt(queue.size()) {
986 generate and insert new configuration
990 that would allow us to do away with region_size altogether.
1004 Interval final_positions = best->y;
1006 #if DEBUG_BEAM_SCORING
1011 for (vsize i = 0; i < configs.size (); i++)
1013 if (configs[i]->done ())
1017 string card = best->score_card_ + to_string (" c%d/%d", completed, configs.size ());
1018 beam_->set_property ("annotation", ly_string2scm (card));
1022 junk_pointers (configs);
1023 return final_positions;
1027 Beam_scoring_problem::score_stem_lengths (Beam_configuration *config) const
1029 Real limit_penalty = parameters_.STEM_LENGTH_LIMIT_PENALTY;
1030 Drul_array<Real> score (0, 0);
1031 Drul_array<int> count (0, 0);
1033 for (vsize i = 0; i < stem_xpositions_.size (); i++)
1035 Real x = stem_xpositions_[i];
1036 Real dx = x_span_.delta ();
1038 ? config->y[RIGHT] * (x - x_span_[LEFT]) / dx + config->y[LEFT] * (x_span_[RIGHT] - x) / dx
1039 : (config->y[RIGHT] + config->y[LEFT]) / 2;
1040 Real current_y = beam_y + base_lengths_[i];
1041 Real length_pen = parameters_.STEM_LENGTH_DEMERIT_FACTOR;
1043 Stem_info info = stem_infos_[i];
1044 Direction d = info.dir_;
1046 score[d] += limit_penalty * max (0.0, (d * (info.shortest_y_ - current_y)));
1048 Real ideal_diff = d * (current_y - info.ideal_y_);
1049 Real ideal_score = shrink_extra_weight (ideal_diff, 1.5);
1051 /* We introduce a power, to make the scoring strictly
1052 convex. Otherwise a symmetric knee beam (up/down/up/down)
1053 does not have an optimum in the middle. */
1055 ideal_score = pow (ideal_score, 1.1);
1057 score[d] += length_pen * ideal_score;
1061 /* Divide by number of stems, to make the measure scale-free. */
1064 score[d] /= max (count[d], 1);
1065 while (flip (&d) != DOWN);
1067 config->add (score[LEFT] + score[RIGHT], "L");
1071 Beam_scoring_problem::score_slope_direction (Beam_configuration *config) const
1073 Real dy = config->y.delta ();
1074 Real damped_dy = unquanted_y_.delta ();
1077 DAMPING_DIRECTION_PENALTY is a very harsh measure, while for
1078 complex beaming patterns, horizontal is often a good choice.
1080 TODO: find a way to incorporate the complexity of the beam in this
1083 if (sign (damped_dy) != sign (dy))
1087 if (fabs (damped_dy / x_span_.delta ()) > parameters_.ROUND_TO_ZERO_SLOPE)
1088 dem += parameters_.DAMPING_DIRECTION_PENALTY;
1090 dem += parameters_.HINT_DIRECTION_PENALTY;
1093 dem += parameters_.DAMPING_DIRECTION_PENALTY;
1096 config->add (dem, "Sd");
1099 // Score for going against the direction of the musical pattern
1101 Beam_scoring_problem::score_slope_musical (Beam_configuration *config) const
1103 Real dy = config->y.delta ();
1104 Real dem = parameters_.MUSICAL_DIRECTION_FACTOR
1105 * max (0.0, (fabs (dy) - fabs (musical_dy_)));
1106 config->add (dem, "Sm");
1109 // Score deviation from calculated ideal slope.
1111 Beam_scoring_problem::score_slope_ideal (Beam_configuration *config) const
1113 Real dy = config->y.delta ();
1114 Real damped_dy = unquanted_y_.delta ();
1117 Real slope_penalty = parameters_.IDEAL_SLOPE_FACTOR;
1119 /* Xstaff beams tend to use extreme slopes to get short stems. We
1120 put in a penalty here. */
1122 slope_penalty *= 10;
1124 /* Huh, why would a too steep beam be better than a too flat one ? */
1125 dem += shrink_extra_weight (fabs (damped_dy) - fabs (dy), 1.5)
1128 config->add (dem, "Si");
1134 return x - floor (x);
1137 // TODO - there is some overlap with forbidden quants, but for
1138 // horizontal beams, it is much more serious to have stafflines
1139 // appearing in the wrong place, so we have a separate scorer.
1141 Beam_scoring_problem::score_horizontal_inter_quants (Beam_configuration *config) const
1143 if (config->y.delta () == 0.0
1144 && abs (config->y[LEFT]) < staff_radius_ * staff_space_)
1146 Real yshift = config->y[LEFT] - 0.5 * staff_space_;
1147 if (fabs (my_round (yshift) - yshift) < 0.01 * staff_space_)
1148 config->add (parameters_.HORIZONTAL_INTER_QUANT_PENALTY, "H");
1153 TODO: The fixed value SECONDARY_BEAM_DEMERIT is probably flawed:
1154 because for 32nd and 64th beams the forbidden quants are relatively
1155 more important than stem lengths.
1158 Beam_scoring_problem::score_forbidden_quants (Beam_configuration *config) const
1160 Real dy = config->y.delta ();
1162 Real extra_demerit = parameters_.SECONDARY_BEAM_DEMERIT
1163 / max (edge_beam_counts_[LEFT], edge_beam_counts_[RIGHT]);
1167 Real eps = parameters_.BEAM_EPS;
1171 for (int j = 1; j <= edge_beam_counts_[d]; j++)
1173 Direction stem_dir = edge_dirs_[d];
1176 The 2.2 factor is to provide a little leniency for
1177 borderline cases. If we do 2.0, then the upper outer line
1178 will be in the gap of the (2, sit) quant, leading to a
1181 Real gap1 = config->y[d] - stem_dir * ((j - 1) * beam_translation_ + beam_thickness_ / 2 - line_thickness_ / 2.2);
1182 Real gap2 = config->y[d] - stem_dir * (j * beam_translation_ - beam_thickness_ / 2 + line_thickness_ / 2.2);
1185 gap.add_point (gap1);
1186 gap.add_point (gap2);
1188 for (Real k = -staff_radius_;
1189 k <= staff_radius_ + eps; k += 1.0)
1190 if (gap.contains (k))
1192 Real dist = min (fabs (gap[UP] - k), fabs (gap[DOWN] - k));
1195 this parameter is tuned to grace-stem-length.ly
1197 Real fixed_demerit = 0.4;
1199 dem += extra_demerit
1201 + (1 - fixed_demerit) * (dist / gap.length ()) * 2);
1205 while ((flip (&d)) != LEFT);
1207 if (max (edge_beam_counts_[LEFT], edge_beam_counts_[RIGHT]) >= 2)
1209 Real straddle = 0.0;
1210 Real sit = (beam_thickness_ - line_thickness_) / 2;
1212 Real hang = 1.0 - (beam_thickness_ - line_thickness_) / 2;
1217 if (edge_beam_counts_[d] >= 2
1218 && fabs (config->y[d] - edge_dirs_[d] * beam_translation_) < staff_radius_ + inter)
1220 // TODO up/down symmetry.
1221 if (edge_dirs_[d] == UP && dy <= eps
1222 && fabs (my_modf (config->y[d]) - sit) < eps)
1223 dem += extra_demerit;
1225 if (edge_dirs_[d] == DOWN && dy >= eps
1226 && fabs (my_modf (config->y[d]) - hang) < eps)
1227 dem += extra_demerit;
1230 if (edge_beam_counts_[d] >= 3
1231 && fabs (config->y[d] - 2 * edge_dirs_[d] * beam_translation_) < staff_radius_ + inter)
1233 // TODO up/down symmetry.
1234 if (edge_dirs_[d] == UP && dy <= eps
1235 && fabs (my_modf (config->y[d]) - straddle) < eps)
1236 dem += extra_demerit;
1238 if (edge_dirs_[d] == DOWN && dy >= eps
1239 && fabs (my_modf (config->y[d]) - straddle) < eps)
1240 dem += extra_demerit;
1243 while (flip (&d) != LEFT);
1246 config->add (dem, "F");
1250 Beam_scoring_problem::score_collisions (Beam_configuration *config) const
1252 Real demerits = 0.0;
1253 for (vsize i = 0; i < collisions_.size (); i++)
1255 Interval collision_y = collisions_[i].y_;
1256 Real x = collisions_[i].x_;
1258 Real center_beam_y = y_at (x, config);
1259 Interval beam_y = center_beam_y + collisions_[i].beam_y_;
1261 Real dist = infinity_f;
1262 if (!intersection (beam_y, collision_y).is_empty ())
1265 dist = min (beam_y.distance (collision_y[DOWN]),
1266 beam_y.distance (collision_y[UP]));
1269 = max (parameters_.COLLISION_PADDING - dist, 0.0) /
1270 parameters_.COLLISION_PADDING;
1272 += collisions_[i].base_penalty_ *
1273 pow (scale_free, 3) * parameters_.COLLISION_PENALTY;
1276 config->add (demerits, "C");