#include "warn.hh"
#include "paper-column.hh"
+/*
+ TODO:
+
+ - curve around flag for y coordinate
+
+ - this file is a big mess, clean it up
+
+
+ - short-cut: try a smaller region first.
+
+ - handle non-visible stems better.
+
+ - try to prune number of scoring criteria
+
+ - take encompass-objects more into account when determining
+ slur shape
+
+ - calculate encompass scoring directly after determining slur shape.
+
+ - optimize.
+
+*/
struct Slur_score
{
}
};
-/*
- TODO: put in details property.,
-
- use lowercase.
-*/
struct Slur_score_parameters
{
int region_size_;
Real max_slope_factor_;
Real extra_object_collision_;
Real accidental_collision_;
+ Real free_slur_distance_;
Real free_head_distance_;
Real extra_encompass_free_distance_;
+ Real edge_slope_exponent_;
+ Real head_slur_distance_max_ratio_;
+ Real head_slur_distance_factor_;
+
+
+
Slur_score_parameters (Grob*);
};
-/*
- TODO:
- - curve around flag for y coordinate
- - better scoring.
- - short-cut: try a smaller region first.
- - collisions with accidentals
- - collisions with articulations (staccato, portato, sforzato, ...)
- - handle non-visible stems better.
-*/
struct Encompass_info
{
Real x_;
stem_ = 0.0;
head_ = 0.0;
}
+ Real get_point (Direction dir)
+ {
+ Interval y;
+ y.add_point (stem_);
+ y.add_point (head_);
+ return y[dir];
+ }
};
struct Bound_info
Interval slur_head_extent_;
Real neighbor_y_;
Real staff_space_;
-
+
Bound_info ()
{
stem_ = 0;
= get_detail (details, ly_symbol2scm ("accidental-collision"));
score_param->extra_encompass_free_distance_
= get_detail (details, ly_symbol2scm ("extra-encompass-free-distance"));
+ score_param->head_slur_distance_factor_
+ = get_detail (details, ly_symbol2scm ("head-slur-distance-factor"));
+ score_param->head_slur_distance_max_ratio_
+ = get_detail (details, ly_symbol2scm ("head-slur-distance-max-ratio"));
+ score_param->free_slur_distance_
+ = get_detail (details, ly_symbol2scm ("free-slur-distance"));
+ score_param->edge_slope_exponent_
+ = get_detail (details, ly_symbol2scm ("edge-slope-exponent"));
}
init_score_param (me, this);
}
-/* HDIR indicates the direction for the slur. */
+/* HDIR indicates which side (left or right) we are processing here. */
Real
broken_trend_y (Grob *me, Grob **common, Direction hdir)
{
Direction vdir = get_grob_direction (me);
Real neighbor_y
= neighbor_col->extent (neighbor_common, Y_AXIS)
- .linear_combination (int(neighbor_cols.size()==1 ? CENTER : vdir))
+ .linear_combination (int(vdir))
- common_next_system->relative_coordinate (neighbor_common, Y_AXIS);
Link_array<Grob> my_cols
get_bound_info (Spanner* me, Grob **common)
{
Drul_array<Bound_info> extremes;
- Direction d = LEFT;
+
+ Direction d = RIGHT;
Direction dir = get_grob_direction (me);
do
extremes[d].staff_space_ = Staff_symbol_referencer
::staff_space (extremes[d].stem_);
}
- else
+ else if (d == RIGHT)
+ /*
+ right side anticipates on the next note.
+ */
extremes[d].neighbor_y_ = broken_trend_y (me, common, d);
}
- while (flip (&d) != LEFT);
+ while (flip (&d) != RIGHT);
return extremes;
}
void
set_end_points (Grob *me)
{
-
-
-
Link_array<Grob> columns
= Pointer_group_interface__extract_grobs (me, (Grob *) 0, "note-columns");
&scores);
Real opt = 1e6;
- int opt_idx = 0;
- // why backwards?
- for (int i = scores.size (); i--;)
+ int opt_idx = -1;
+ for (int i = 0; i < scores.size (); i++)
{
if (scores[i].score_ < opt)
{
#if DEBUG_SLUR_QUANTING
SCM inspect_quants = me->get_property ("inspect-quants");
if (to_boolean (me->get_paper ()
- ->lookup_variable (ly_symbol2scm ("debug-slur-quanting")))
+ ->lookup_variable (ly_symbol2scm ("debug-slur-scoring")))
&& ly_c_pair_p (inspect_quants))
{
Drul_array<Real> ins = ly_scm2interval (inspect_quants);
{
Offset o = b.control_[i]
- Offset (me->relative_coordinate (common[X_AXIS], X_AXIS),
- me->relative_coordinate (common[Y_AXIS], Y_AXIS));
+ me->relative_coordinate (common[Y_AXIS], Y_AXIS));
controls = scm_cons (ly_offset2scm (o), controls);
}
me->set_property ("control-points", controls);
>? (dir * base_attachment[-d][Y_AXIS]));
}
else
- end_ys[d] = extremes[d].neighbor_y_ + score_param->region_size_ * dir;
+ end_ys[d] = base_attachment[d][Y_AXIS] + score_param->region_size_ * dir;
}
while (flip (&d) != LEFT);
Drul_array<Offset> base_attachment;
Real staff_space = Staff_symbol_referencer::staff_space ((Grob *) me);
Direction dir = get_grob_direction (me);
- Direction d = LEFT;
+ Direction d = RIGHT;
do
{
Grob *stem = extremes[d].stem_;
Real x, y;
if (!extremes[d].note_column_)
{
- y = extremes[d].neighbor_y_;
- if (d== RIGHT)
- x = extremes[d].bound_->extent (common[X_AXIS], X_AXIS)[d];
+ if (d == RIGHT)
+ {
+ y = extremes[d].neighbor_y_;
+ x = extremes[d].bound_->extent (common[X_AXIS], X_AXIS)[d];
+ }
else
- x = me->get_broken_left_end_align ();
+ {
+ x = me->get_broken_left_end_align ();
+ if (extremes[RIGHT].bound_ == columns[0])
+ {
+ y = base_attachment[RIGHT][Y_AXIS];
+ }
+ else
+ {
+ y = columns[0]->extent (common[Y_AXIS], Y_AXIS)[dir];
+ }
+ }
}
else
{
}
base_attachment[d] = Offset (x, y);
- } while (flip (&d) != LEFT);
+ } while (flip (&d) != RIGHT);
return base_attachment;
}
(void) extremes;
Real staff_space = Staff_symbol_referencer::staff_space ((Grob *) me);
Real r_0 = robust_scm2double (me->get_property ("ratio"), 0.33);
- Real h_inf = staff_space * ly_scm2double (me->get_property ("height-limit"));
+ Real h_inf = staff_space * scm_to_double (me->get_property ("height-limit"));
for (int i = 0; i < scores->size(); i++)
{
- Bezier bez= get_bezier (me,
+ Bezier bez = get_bezier (me,
common,
score_param,
extremes,
(*scores)[i].attachment_, r_0, h_inf);
+
bez = avoid_staff_line (me, common, extremes, bez);
(*scores)[i].attachment_[LEFT] = bez.control_[0];
(*scores)[i].attachment_[RIGHT] = bez.control_[3];
* 5 * thick;
Real dy = (newp - p) * staff_space / 2.0;
-#if 0
- bez.translate (Offset (0, dy));
-#else
+
bez.control_[1][Y_AXIS] += dy;
bez.control_[2][Y_AXIS] += dy;
-
-#endif
}
}
return bez;
os[LEFT][Y_AXIS] += dir * staff_space / 2;
}
+
+ assert (scores.size() > 0);
return scores;
}
+inline Real
+linear_interpolate (Real x, Real x1, Real x2, Real y1, Real y2)
+{
+ return (x2 - x) / (x2 - x1) * y1 +
+ (x - x1) / (x2 - x1) * y2 ;
+}
+
+
void
score_encompass (Grob *me, Grob *common[],
Slur_score_parameters *score_param,
for (int i = 0; i < scores->size (); i++)
{
- Bezier const &bez (scores->elem (i).curve_);
+ Slur_score &configuration = scores->elem_ref (i);
+ Bezier const &bez (configuration.curve_);
Real demerit = 0.0;
+
+ /*
+ Distances for heads that are between slur and line between
+ attachment points.
+ */
+ Array<Real> convex_head_distances;
+ Array<Real> edge_distances;
for (int j = 0; j < infos.size(); j++)
{
Real x = infos[j].x_;
bool r_edge = j==infos.size()-1;
bool edge = l_edge || r_edge;
- if (!(x < scores->elem (i).attachment_[RIGHT][X_AXIS]
- && x > scores->elem (i).attachment_[LEFT][X_AXIS]))
+
+ if (edge)
+ {
+ edge_distances.push (fabs (configuration.attachment_[l_edge ? LEFT : RIGHT][Y_AXIS]
+ - infos[j].get_point (dir)));
+ }
+
+
+ if (!(x < configuration.attachment_[RIGHT][X_AXIS]
+ && x > configuration.attachment_[LEFT][X_AXIS]))
continue;
Real y = bez.get_other_coordinate (X_AXIS, x);
{
Real head_dy = (y - infos[j].head_);
if (dir * head_dy < 0)
- demerit += score_param->head_encompass_penalty_;
+ {
+ demerit += score_param->head_encompass_penalty_;
+ convex_head_distances.push (0.0);
+ }
else
{
Real hd = (head_dy)
: score_param->head_encompass_penalty_;
hd = (hd >? 0)<? score_param->head_encompass_penalty_;
- demerit += hd;
+ demerit += hd;
+ }
+
+ Real line_y = linear_interpolate (x,
+ configuration.attachment_[RIGHT][X_AXIS],
+ configuration.attachment_[LEFT][X_AXIS],
+ configuration.attachment_[RIGHT][Y_AXIS],
+ configuration.attachment_[LEFT][Y_AXIS]);
+
+ if ( 1 ) // dir * infos[j].get_point (dir) > dir *line_y )
+ {
+
+ Real closest =
+ dir * (dir * infos[j].get_point (dir)
+ >? dir *line_y
+ );
+ Real d = fabs(closest - y);
+
+ convex_head_distances.push (d);
}
- }
+ }
+
+
if (dir * (y - infos[j].stem_) < 0)
{
}
}
+ Real variance_penalty = 0.0;
+
+ if (convex_head_distances.size())
+ {
+ Real avg_distance = 0.0;
+ Real min_dist = infinity_f;
+ for (int j = 0; j < convex_head_distances.size(); j++)
+ {
+ min_dist = min_dist <? convex_head_distances[j];
+ avg_distance += convex_head_distances[j];
+ }
+
+ /*
+ For slurs over 3 or 4 heads, the average distance is not a
+ good normalizer.
+ */
+ int n = convex_head_distances.size();
+ if (convex_head_distances.size() <= 2)
+ {
+ // Real min_edge_dist = 1e6;
+ for (int j = 0; j < edge_distances.size(); j++)
+ {
+ avg_distance += edge_distances[j];
+ n++;
+ }
+
+ }
+
+ /*
+ TODO: maybe it's better to use (avgdist - mindist)*factor
+ as penalty.
+ */
+ avg_distance /= n;
+ variance_penalty = score_param->head_slur_distance_max_ratio_;
+ if (min_dist > 0.0)
+ variance_penalty = ((avg_distance / (min_dist +score_param->free_head_distance_)) - 1.0)
+ <? variance_penalty;
+
+ variance_penalty *= score_param->head_slur_distance_factor_;
+ }
#if DEBUG_SLUR_QUANTING
(*scores)[i].score_card_ += to_string ("C%.2f", demerit);
+ (*scores)[i].score_card_ += to_string ("D%.2f", variance_penalty);
#endif
- (*scores)[i].score_ += demerit;
+ (*scores)[i].score_ += demerit + variance_penalty;
}
}
+struct Extra_collision_info
+{
+ Real idx_;
+ Box extents_;
+ Real penalty_;
+
+ Extra_collision_info (Real idx, Interval x, Interval y, Real p)
+ {
+ idx_ = idx;
+ extents_[X_AXIS] = x;
+ extents_[Y_AXIS] = y;
+ penalty_ = p;
+ }
+ Extra_collision_info ()
+ {
+ idx_ = 0.0;
+ penalty_ = 0.;
+ }
+};
+
void
score_extra_encompass (Grob *me, Grob *common[],
Slur_score_parameters *score_param,
(void) base_attach;
(void) extremes;
+ Spanner *me_spanner = dynamic_cast<Spanner*> (me);
+
Link_array<Grob> encompasses
= Pointer_group_interface__extract_grobs (me, (Grob *)0,
"encompass-objects");
Real lt = me->get_paper ()->get_dimension (ly_symbol2scm ("linethickness"));
Real thick = robust_scm2double (me->get_property ("thickness"), 1.0) * lt;
- Array<Real> xidxs;
- Array<Interval> yexts;
- Array<Interval> xexts;
-
+ Array<Extra_collision_info> collision_infos;
for (int i = encompasses.size (); i--; )
{
if (Slur::has_interface (encompasses[i]))
{
- Grob * small_slur = encompasses[i];
+ Spanner * small_slur = dynamic_cast<Spanner*> (encompasses[i]);
Bezier b = Slur::get_curve (small_slur);
- Offset z = b.curve_point (0.5);
- z += Offset (small_slur->relative_coordinate (common[X_AXIS], X_AXIS),
- small_slur->relative_coordinate (common[Y_AXIS], Y_AXIS));
-
- xexts.push (Interval (z[X_AXIS], z[X_AXIS]));
- xidxs.push (0.0);
- yexts.push (z[Y_AXIS] + thick * Interval (-0.5, 0.5));
+ Offset relative (small_slur->relative_coordinate (common[X_AXIS], X_AXIS),
+ small_slur->relative_coordinate (common[Y_AXIS], Y_AXIS));
+
+ for (int k = 0; k < 3; k++)
+ {
+ Direction hdir = Direction (k /2 - 1);
+
+ /*
+ Only take bound into account if small slur starts
+ together with big slur.
+ */
+ if (hdir && small_slur->get_bound (hdir) != me_spanner->get_bound (hdir))
+ continue;
+
+
+ Offset z = b.curve_point ( k / 2.0);
+ z += relative;
+
+ Interval yext;
+ yext.set_full();
+ yext[dir] = z[Y_AXIS] + dir * thick * 1.0;
+
+ Interval xext(-1, 1);
+ xext = xext *(thick*2) + z[X_AXIS];
+ Extra_collision_info info (k - 1.0,
+ xext,
+ yext,
+ score_param->extra_object_collision_);
+ collision_infos.push (info);
+ }
}
else
{
Interval ye = g->extent (common[Y_AXIS], Y_AXIS);
Real xp = 0.0;
-
+ Real penalty = score_param->extra_object_collision_;
if (Accidental_interface::has_interface (g))
{
+ penalty = score_param->accidental_collision_;
/* Begin copy accidental.cc */
bool parens = false;
if (to_boolean (g->get_property ("cautionary")))
SCM accs = g->get_property ("accidentals");
SCM scm_style = g->get_property ("style");
- if (!ly_c_symbol_p (scm_style)
+ if (!scm_is_symbol (scm_style)
&& !parens
&& scm_ilength (accs) == 1)
{
/* End copy accidental.cc */
- switch (ly_scm2int (ly_car (accs)))
+ switch (scm_to_int (ly_car (accs)))
{
case FLAT:
case DOUBLE_FLAT:
}
}
- xidxs.push (xp);
ye.widen (thick * 0.5);
- yexts.push (ye);
- xexts.push (xe);
+ xe.widen (thick * 1.0);
+ Extra_collision_info info (xp, xe, ye, penalty);
+ collision_infos.push (info);
}
}
for (int i = 0; i < scores->size (); i++)
{
Real demerit = 0.0;
- for (int j = 0; j < xidxs.size(); j++)
+ for (int j = 0; j < collision_infos.size(); j++)
{
- Drul_array<Offset> at = scores->elem (i).attachment_;
- Interval slur_wid (at[LEFT][X_AXIS], at[RIGHT][X_AXIS]);
+ Drul_array<Offset> attachment = scores->elem (i).attachment_;
+ Interval slur_wid (attachment[LEFT][X_AXIS], attachment[RIGHT][X_AXIS]);
/*
to prevent numerical inaccuracies in
Bezier::get_other_coordinate().
- */
- slur_wid.widen (- 0.5 * thick);
- Real x = xexts[j].linear_combination (xidxs[j]);
+ */
+ Direction d = LEFT;
+ bool found = false;
Real y = 0.0;
- if (!slur_wid.contains (x))
- {
- Direction contains_dir = CENTER;
- Direction d = LEFT;
- do
+
+ do
+ {
+ if (collision_infos[j].extents_[X_AXIS].contains (attachment[d][X_AXIS]))
{
- if (xexts[j].contains (at[d][X_AXIS]))
- contains_dir = d;
+ y = attachment[d][Y_AXIS];
+ found = true;
}
- while (flip (&d) != LEFT);
-
- if (!contains_dir)
- continue;
- else
- y = at[contains_dir][Y_AXIS];
}
- else
+ while (flip (&d) != LEFT);
+
+ if (!found)
{
+
+ Real x = collision_infos[j].extents_[X_AXIS]
+ .linear_combination (collision_infos[j].idx_);
+
+ if (!slur_wid.contains (x))
+ continue;
+
y = scores->elem (i).curve_.get_other_coordinate (X_AXIS, x);
}
- Real collision_demerit =
- (Accidental_interface::has_interface (encompasses[j]))
- ? score_param->accidental_collision_
- : score_param->extra_object_collision_;
-
- Real dist = yexts[j].distance (y);
+ Real dist = collision_infos[j].extents_[Y_AXIS].distance (y);
demerit +=
fabs (0 >? (score_param->extra_encompass_free_distance_ - dist)) /
- score_param->extra_encompass_free_distance_ * collision_demerit;
+ score_param->extra_encompass_free_distance_
+ * collision_infos[j].penalty_;
}
#if DEBUG_SLUR_QUANTING
(*scores)[i].score_card_ += to_string ("X%.2f", demerit);
}
}
+/*
+ TODO: should make edge penalties dependent on the direction that the
+ slur-end is pointing.
+ */
void
score_edges (Grob *me, Grob *common[],
Slur_score_parameters * score_param,
for (int i = 0; i < scores->size (); i++)
{
Direction d = LEFT;
+ Slur_score &config = scores->elem_ref (i);
+ Offset dz = config.attachment_[RIGHT] - config.attachment_[LEFT];
+ Real slope = dz[Y_AXIS] / dz[X_AXIS];
do
{
- Real y = scores->elem (i).attachment_[d][Y_AXIS];
+ Real y = config.attachment_[d][Y_AXIS];
Real dy = fabs (y - base_attach[d][Y_AXIS]);
Real factor = score_param->edge_attraction_factor_;
&& !Stem::get_beaming (extremes[d].stem_, -d)
)
demerit /= 5;
-
+
+ demerit *= exp (dir * d * slope
+ * score_param->edge_slope_exponent_ );
+
(*scores)[i].score_ += demerit;
#if DEBUG_SLUR_QUANTING
(*scores)[i].score_card_ += to_string ("E%.2f", demerit);
Slur_score_parameters *score_param,
Drul_array<Bound_info> extremes,
Drul_array<Offset> attachments,
- Real r_0, Real h_inf)
+ Real r_0, Real h_inf
+ )
{
Link_array<Grob> encompasses
= Pointer_group_interface__extract_grobs (me, (Grob *)0, "note-columns");
Direction dir = get_grob_direction (me);
- Array<Offset> avoid;
+ Array<Offset> avoid;
for (int i = 0; i < encompasses.size(); i++)
{
if (extremes[LEFT].note_column_ == encompasses[i]
avoid.push (Offset (inf.x_, y + dir * score_param->free_head_distance_));
}
+
+ Link_array<Grob> extra_encompasses
+ = Pointer_group_interface__extract_grobs (me, (Grob *)0, "encompass-objects");
+ for (int i = 0; i < extra_encompasses.size (); i++)
+ if (Slur::has_interface (extra_encompasses[i]))
+ {
+ Grob * small_slur = extra_encompasses[i];
+ Bezier b = Slur::get_curve (small_slur);
+
+ Offset z = b.curve_point (0.5);
+ z += Offset (small_slur->relative_coordinate (common[X_AXIS], X_AXIS),
+ small_slur->relative_coordinate (common[Y_AXIS], Y_AXIS));
+
+ z[Y_AXIS] += dir * score_param->free_slur_distance_;
+ avoid.push (z);
+ }
Offset dz = attachments[RIGHT]- attachments[LEFT];;
Offset dz_unit = dz;