#include "beam.hh"
+#include "align-interface.hh"
#include "beam-scoring-problem.hh"
#include "beaming-pattern.hh"
#include "directional-element-interface.hh"
Spanner *me = unsmob_spanner (grob);
Grob *commonx = 0;
vector<Beam_segment> segments = get_beam_segments (me, &commonx);
+ if (!segments.size ())
+ return SCM_EOL;
Interval span;
if (normal_stem_count (me))
Direction feather_dir = to_dir (me->get_property ("grow-direction"));
+ Interval placements = robust_scm2interval (me->get_property ("normalized-endpoints"), Interval (0.0, 0.0));
+
Stencil the_beam;
+
+ int extreme = (segments[0].vertical_count_ == 0
+ ? segments[0].vertical_count_
+ : segments.back ().vertical_count_);
+
for (vsize i = 0; i < segments.size (); i ++)
{
Real local_slope = slope;
+ /*
+ Makes local slope proportional to the ratio of the length of this beam
+ to the total length.
+ */
if (feather_dir)
- {
- local_slope += feather_dir * segments[i].vertical_count_ * beam_dy / span.length ();
- }
+ local_slope += (feather_dir * segments[i].vertical_count_
+ * beam_dy
+ * placements.length ()
+ / span.length ());
Stencil b = Lookup::beam (local_slope, segments[i].horizontal_.length (), beam_thickness, blot);
b.translate_axis (segments[i].horizontal_[LEFT], X_AXIS);
+ Real multiplier = feather_dir ? placements[LEFT] : 1.0;
+
+ Interval weights (1 - multiplier, multiplier);
+
+ if (feather_dir != LEFT)
+ weights.swap ();
+
+ // we need two translations: the normal one and
+ // the one of the lowest segment
+ int idx[] = {i, extreme};
+ Real translations[2];
+
+ for (int j = 0; j < 2; j++)
+ translations[j] = slope
+ * (segments[idx[j]].horizontal_[LEFT] - span.linear_combination (CENTER))
+ + pos.linear_combination (CENTER)
+ + beam_dy * segments[idx[j]].vertical_count_;
+
+ Real weighted_average = translations[0] * weights[LEFT] + translations[1] * weights[RIGHT];
+
+ /*
+ Tricky. The manipulation of the variable `weighted_average' below ensures
+ that beams with a RIGHT grow direction will start from the position of the
+ lowest segment at 0, and this error will decrease and decrease over the
+ course of the beam. Something with a LEFT grow direction, on the other
+ hand, will always start in the correct place but progressively accrue
+ error at broken places. This code shifts beams up given where they are
+ in the total span length (controlled by the variable `multiplier'). To
+ better understand what it does, try commenting it out: you'll see that
+ all of the RIGHT growing beams immediately start too low and get better
+ over line breaks, whereas all of the LEFT growing beams start just right
+ and get worse over line breaks.
+ */
+ Real factor = Interval (multiplier, 1 - multiplier).linear_combination (feather_dir);
+
+ if (segments[0].vertical_count_ < 0 && feather_dir)
+ weighted_average += beam_dy * (segments.size () - 1) * factor;
+
+ b.translate_axis (weighted_average, Y_AXIS);
- b.translate_axis (local_slope
- * (segments[i].horizontal_[LEFT] - span.linear_combination (feather_dir))
- + pos.linear_combination (feather_dir)
- + beam_dy * segments[i].vertical_count_, Y_AXIS);
the_beam.add_stencil (b);
+
}
#if (DEBUG_BEAM_SCORING)
properties = scm_cons(scm_acons (ly_symbol2scm ("font-size"), scm_from_int (-5), SCM_EOL),
properties);
-
+
Direction stem_dir = stems.size () ? to_dir (stems[0]->get_property ("direction")) : UP;
Stencil score = *unsmob_stencil (Text_interface::interpret_markup
}
Direction dir = get_grob_direction (me);
- Real y = head_positions[dir]
+
+ if (!dir)
+ programming_error ("The beam should have a direction by now.");
+
+ Real y = head_positions.linear_combination (dir)
* 0.5 * Staff_symbol_referencer::staff_space (me)
+ dir * get_beam_translation (me) * (multiplicity.length () + 1);
Real x = s->relative_coordinate (common[X_AXIS], X_AXIS) - x_span[LEFT];
x_posns.push_back (x);
}
-
+
Grob *lvs = last_normal_stem (me);
x_span[RIGHT] = lvs->relative_coordinate (common[X_AXIS], X_AXIS);
feasible_left_point.intersect (flp);
}
- /*
- We have two intervals here, one for the up variant (beams goes
- over the collision) one for the down.
- */
- Drul_array<Interval> collision_free (feasible_left_point,
- feasible_left_point);
-
vector<Grob*> filtered;
/*
We only update these for objects that are too large for quanting
take care of computing the impact those exactly.
*/
Real min_y_size = 2.0;
+
+ // A list of intervals into which beams may not fall
+ vector<Interval> forbidden_intervals;
+
for (vsize i = 0; i < covered.size(); i++)
{
if (!covered[i]->is_live())
continue;
-
+
+ if (Beam::has_interface (covered[i]) && is_cross_staff (covered[i]))
+ continue;
+
Box b;
for (Axis a = X_AXIS; a < NO_AXES; incr (a))
b[a] = covered[i]->extent (common[a], a);
the centerline.
*/
Direction stemdir = get_grob_direction (head_stem);
- b[Y_AXIS][stemdir] = stemdir * infinity_f;
+ b[Y_AXIS][stemdir] = stemdir * infinity_f;
}
else
{
Real dy = slope * x;
Direction yd = DOWN;
+ Interval disallowed;
do
{
Real left_y = b[Y_AXIS][yd];
- if (left_y == yd * infinity_f)
- {
- collision_free[yd].set_empty ();
- continue;
- }
-
left_y -= dy;
// Translate back to beam as ref point.
- left_y -= -me->relative_coordinate (common[Y_AXIS], Y_AXIS);
-
- Interval allowed;
- allowed.set_full ();
+ left_y -= me->relative_coordinate (common[Y_AXIS], Y_AXIS);
- allowed[-yd] = left_y;
- collision_free[yd].intersect (allowed);
+ disallowed[yd] = left_y;
}
while (flip (&yd) != DOWN);
+
+ forbidden_intervals.push_back (disallowed);
}
while (flip (&d) != LEFT);
}
- Grob_array *arr =
+ Grob_array *arr =
Pointer_group_interface::get_grob_array (me,
ly_symbol2scm ("covered-grobs"));
arr->set_array (filtered);
- if (collision_free[DOWN].contains (beam_left_y)
- || collision_free[UP].contains (beam_left_y))
+ vector_sort (forbidden_intervals, Interval::left_less);
+ Real epsilon = 1.0e-10;
+ Interval feasible_beam_placements (beam_left_y, beam_left_y);
+
+ /*
+ forbidden_intervals contains a vector of intervals in which
+ the beam cannot start. it iterates through these intervals,
+ pushing feasible_beam_placements epsilon over or epsilon under a
+ collision. when this type of change happens, the loop is marked
+ as "dirty" and re-iterated.
+
+ TODO: figure out a faster ways that this loop can happen via
+ a better search algorithm and/or OOP.
+ */
+
+ bool dirty = false;
+ do
{
- // We're good to go. Do nothing.
+ dirty = false;
+ for (vsize i = 0; i < forbidden_intervals.size (); i++)
+ {
+ Direction d = DOWN;
+ do
+ {
+ if (forbidden_intervals[i][d] == d * infinity_f)
+ feasible_beam_placements[d] = d * infinity_f;
+ else if (forbidden_intervals[i].contains (feasible_beam_placements[d]))
+ {
+ feasible_beam_placements[d] = d * epsilon + forbidden_intervals[i][d];
+ dirty = true;
+ }
+ }
+ while (flip (&d) != DOWN);
+ }
}
- else if (!collision_free[DOWN].is_empty ()
- || !collision_free[UP].is_empty ())
- {
- // We have space above or below collisions (or, no collisions at
- // all).
- Interval best =
- (collision_free[DOWN].length () > collision_free[UP].length ()) ?
- collision_free[DOWN] : collision_free[UP];
+ while (dirty);
- beam_left_y = point_in_interval (best, 2.0);
+ // if the beam placement falls out of the feasible region, we push it
+ // to infinity so that it can never be a feasible candidate below
+ Direction d = DOWN;
+ do
+ {
+ if (!feasible_left_point.contains (feasible_beam_placements[d]))
+ feasible_beam_placements[d] = d*infinity_f;
}
- else if (!feasible_left_point.is_empty ())
+ while (flip (&d) != DOWN);
+
+ if ((feasible_beam_placements[UP] == infinity_f && feasible_beam_placements[DOWN] == -infinity_f) && !feasible_left_point.is_empty ())
{
// We are somewhat screwed: we have a collision, but at least
// there is a way to satisfy stem length constraints.
beam_left_y = point_in_interval (feasible_left_point, 2.0);
}
+ else if (!feasible_left_point.is_empty ())
+ {
+ // Only one of them offers is feasible solution. Pick that one.
+ if (abs (beam_left_y - feasible_beam_placements[DOWN]) > abs (beam_left_y - feasible_beam_placements[UP]))
+ beam_left_y = feasible_beam_placements[UP];
+ else
+ beam_left_y = feasible_beam_placements[DOWN];
+ }
else
{
// We are completely screwed.
- warning (_ ("no viable initial configuration found: may not find good beam slope"));
+ me->warning (_ ("no viable initial configuration found: may not find good beam slope"));
}
-
+
pos = Drul_array<Real> (beam_left_y, (beam_left_y + beam_dy));
scale_drul (&pos, 1 / Staff_symbol_referencer::staff_space (me));
"break-overshoot "
"clip-edges "
"concaveness "
+ "collision-interfaces "
+ "collision-voice-only "
"covered-grobs "
"damping "
"details "