@end lilypond
@example
-r4 <c e g>8\>( <c e g> <c e g> <c f a>8\!)
+r4 <c e g>8\>( <c e g> <c e g> <c f a>\!)
@end example
@lilypond[quote,relative=1,fragment]
\slurUp
-r4 <c e g>8\>( <c e g> <c e g> <c f a>8\!)
+r4 <c e g>8\>( <c e g> <c e g> <c f a>\!)
@end lilypond
-
-
@node Advanced rhythmic commands
@section Advanced rhythmic commands
< ly_scm2int (script_->get_property ("script-priority")))
Side_position_interface::add_support (line_spanner_, info.grob_);
}
- else if (New_slur::has_interface (info.grob_))
+ else if (Slur::has_interface (info.grob_))
slur_ = dynamic_cast<Spanner*> (info.grob_);
}
+++ /dev/null
-/*
- slur.cc -- implement external interface for Slur
-
- source file of the GNU LilyPond music typesetter
-
- (c) 1996--2004 Han-Wen Nienhuys <hanwen@cs.uu.nl>
- Jan Nieuwenhuizen <janneke@gnu.org>
-*/
-
-
-#include <math.h>
-
-#include "bezier.hh"
-#include "new-slur.hh"
-#include "main.hh"
-#include "font-interface.hh"
-#include "text-item.hh"
-#include "directional-element-interface.hh"
-#include "group-interface.hh"
-#include "lily-guile.hh"
-#include "lookup.hh"
-#include "note-column.hh"
-#include "output-def.hh"
-#include "rod.hh"
-#include "spanner.hh"
-#include "staff-symbol-referencer.hh"
-#include "staff-symbol.hh"
-#include "stem.hh"
-#include "stencil.hh"
-#include "warn.hh"
-#include "beam.hh"
-
-MAKE_SCHEME_CALLBACK (New_slur, height, 2);
-SCM
-New_slur::height (SCM smob, SCM ax)
-{
- Axis a = (Axis)ly_scm2int (ax);
- Grob *me = unsmob_grob (smob);
- assert (a == Y_AXIS);
-
- SCM mol = me->get_uncached_stencil ();
- Interval ext;
- if (Stencil *m = unsmob_stencil (mol))
- ext = m->extent (a);
- return ly_interval2scm (ext);
-}
-
-/*
- Ugh should have dash-length + dash-period
-*/
-MAKE_SCHEME_CALLBACK (New_slur, print,1);
-SCM
-New_slur::print (SCM smob)
-{
- Grob *me = unsmob_grob (smob);
- if (!scm_ilength (me->get_property ("note-columns")))
- {
- me->suicide ();
- return SCM_EOL;
- }
-
- Real base_thick = robust_scm2double (me->get_property ("thickness"), 1);
- Real thick = base_thick * Staff_symbol_referencer::line_thickness (me);
-
- Real ss = Staff_symbol_referencer::staff_space (me);
- Bezier one = get_curve (me);
-
- Stencil a;
-
- /*
- TODO: replace dashed with generic property.
- */
- SCM d = me->get_property ("dashed");
- if (ly_c_number_p (d))
- a = Lookup::dashed_slur (one, thick, thick * robust_scm2double (d, 0));
- else
- a = Lookup::slur (one, get_grob_direction (me) * base_thick * ss / 10.0,
- thick);
-
-#if DEBUG_SLUR_QUANTING
- SCM quant_score = me->get_property ("quant-score");
-
- if (to_boolean (me->get_paper ()
- ->lookup_variable (ly_symbol2scm ("debug-slur-quanting")))
- && ly_c_string_p (quant_score))
- {
- String str;
- SCM properties = Font_interface::text_font_alist_chain (me);
-
- Stencil tm = *unsmob_stencil (Text_item::interpret_markup
- (me->get_paper ()->self_scm (), properties,
- quant_score));
- a.add_at_edge (Y_AXIS, get_grob_direction (me), tm, 1.0, 0);
- }
-#endif
-
- return a.smobbed_copy ();
-}
-
-
-Bezier
-New_slur::get_curve (Grob*me)
-{
- Bezier b;
- int i = 0;
- for (SCM s = me->get_property ("control-points"); s != SCM_EOL;
- s = ly_cdr (s))
- b.control_[i++] = ly_scm2offset (ly_car (s));
-
- return b;
-}
-
-void
-New_slur::add_column (Grob*me, Grob*n)
-{
- Pointer_group_interface::add_grob (me, ly_symbol2scm ("note-columns"), n);
- add_bound_item (dynamic_cast<Spanner*> (me), dynamic_cast<Item*> (n));
-}
-
-
-void
-New_slur::add_extra_encompass (Grob*me, Grob*n)
-{
- Pointer_group_interface::add_grob (me, ly_symbol2scm ("encompass-objects"), n);
-}
-
-
-
-MAKE_SCHEME_CALLBACK (New_slur, outside_slur_callback, 2);
-SCM
-New_slur::outside_slur_callback (SCM grob, SCM axis)
-{
- Grob *script = unsmob_grob (grob);
- Axis a = Axis (ly_scm2int (axis));
- assert (a == Y_AXIS);
-
- Grob *slur = unsmob_grob (script->get_property ("slur"));
-
- if (!slur)
- return scm_from_int (0);
-
- Grob *cx = script->common_refpoint (slur, X_AXIS);
- Grob *cy = script->common_refpoint (slur, Y_AXIS);
-
- Bezier curve = New_slur::get_curve (slur);
-
- curve.translate (Offset (slur->relative_coordinate (cx, X_AXIS),
- slur->relative_coordinate (cy, Y_AXIS)));
-
- Interval yext = robust_relative_extent (script, cy, Y_AXIS);
- Interval xext = robust_relative_extent (script, cx, X_AXIS);
-
-
- Real slur_padding = 0.2; // todo: slur property, script property?
- yext.widen (slur_padding);
-
- Interval bezext (curve.control_[0][X_AXIS],
- curve.control_[3][X_AXIS]);
-
- Real x = xext.center ();
- if (!bezext.contains (xext[RIGHT]))
- x = xext[LEFT];
- else if (!bezext.contains (xext[LEFT]))
- x = xext[RIGHT];
-
-
- if (!bezext.contains (x))
- return scm_make_real (0);
-
- Real y = curve.get_other_coordinate (X_AXIS, x);
- if (yext.contains (y))
- {
- Direction dir = get_grob_direction (script);
- return scm_make_real (y - yext[-dir] + dir * slur_padding);
- }
- return scm_make_real (0.0);
-}
-
-
-ADD_INTERFACE (New_slur, "new-slur-interface",
- "A slur",
- "excentricity encompass-objects control-points dashed slur-details direction height-limit note-columns ratio thickness");
-
if (Note_column::has_interface (info.grob_))
{
for (int i = slurs_.size (); i--; )
- New_slur::add_column (slurs_[i], e);
+ Slur::add_column (slurs_[i], e);
for (int i = end_slurs_.size (); i-- ; )
- New_slur::add_column (end_slurs_[i], e);
+ Slur::add_column (end_slurs_[i], e);
}
else
{
TODO: maybe take more objects?
*/
for (int i = slurs_.size (); i--; )
- New_slur::add_extra_encompass (slurs_[i], e);
+ Slur::add_extra_encompass (slurs_[i], e);
for (int i = end_slurs_.size (); i--; )
- New_slur::add_extra_encompass (end_slurs_[i], e);
+ Slur::add_extra_encompass (end_slurs_[i], e);
}
}
e->set_parent (info.grob_, X_AXIS);
}
}
- else if (New_slur::has_interface (info.grob_))
+ else if (Slur::has_interface (info.grob_))
slur_ = dynamic_cast<Spanner*> (info.grob_);
}
+++ /dev/null
-/*
- slur-quanting.cc -- Score based slur formatting
-
- source file of the GNU LilyPond music typesetter
-
- (c) 1996--2004 Han-Wen Nienhuys <hanwen@cs.uu.nl>
- Jan Nieuwenhuizen <janneke@gnu.org>
-*/
-
-#include <math.h>
-
-#include "accidental-interface.hh"
-#include "beam.hh"
-#include "directional-element-interface.hh"
-#include "group-interface.hh"
-#include "libc-extension.hh"
-#include "lily-guile.hh"
-#include "new-slur.hh"
-#include "note-column.hh"
-#include "output-def.hh"
-#include "pitch.hh"
-#include "bezier.hh"
-#include "spanner.hh"
-#include "staff-symbol-referencer.hh"
-#include "staff-symbol.hh"
-#include "stem.hh"
-#include "warn.hh"
-#include "paper-column.hh"
-
-
-struct Slur_score
-{
- Drul_array<Offset> attachment_;
- Real score_;
- Bezier curve_;
-
-#if DEBUG_SLUR_QUANTING
- String score_card_;
-#endif
-
- Slur_score()
- {
- score_ = 0.0;
- }
-};
-
-/*
- TODO: put in details property.,
-
- use lowercase.
-*/
-struct Slur_score_parameters
-{
- int region_size_;
- Real head_encompass_penalty_;
- Real stem_encompass_penalty_;
- Real closeness_factor_;
- Real edge_attraction_factor_;
- Real same_slope_penalty_;
- Real steeper_slope_factor_;
- Real non_horizontal_penalty_;
- Real max_slope_;
- Real max_slope_factor_;
- Real extra_object_collision_;
- Real accidental_collision_;
- Real free_head_distance_;
- Real extra_encompass_free_distance_;
- 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_;
- Real stem_;
- Real head_;
- Encompass_info ()
- {
- x_ = 0.0;
- stem_ = 0.0;
- head_ = 0.0;
- }
-};
-
-struct Bound_info
-{
- Box stem_extent_;
- Direction stem_dir_;
- Item *bound_;
- Grob *note_column_;
- Grob *slur_head_;
- Grob *staff_;
- Grob *stem_;
- Interval slur_head_extent_;
- Real neighbor_y_;
- Real staff_space_;
-
- Bound_info ()
- {
- stem_ = 0;
- neighbor_y_ = 0;
- staff_ = 0;
- slur_head_ = 0;
- stem_dir_ = CENTER;
- note_column_ = 0;
- }
-};
-
-/*
- TODO: create one object for passing all parameters.
- */
-
-
-static void
-score_extra_encompass (Grob *me, Grob *common[],
- Slur_score_parameters *score_param,
- Drul_array<Bound_info> ,
- Drul_array<Offset> ,
- Array<Slur_score> * scores);
-static void score_slopes (Grob *me, Grob *common[],
- Slur_score_parameters *score_param,
- Drul_array<Bound_info>,
- Drul_array<Offset> base_attach,
- Array<Slur_score> *scores);
-
-static void score_edges (Grob *me, Grob *common[],
- Slur_score_parameters *score_param,
- Drul_array<Bound_info> extremes,
- Drul_array<Offset> base_attach,
- Array<Slur_score> *scores);
-static void score_encompass (Grob *me, Grob *common[],
- Slur_score_parameters*,
- Drul_array<Bound_info>,
- Drul_array<Offset>, Array<Slur_score> *scores);
-static Bezier avoid_staff_line (Grob *me, Grob **common,
- Drul_array<Bound_info> extremes,
- Bezier bez);
-
-static Encompass_info get_encompass_info (Grob *me,
- Grob *col,
- Grob **common);
-static Bezier get_bezier (Grob *me,
- Grob **common,
- Slur_score_parameters*,
- Drul_array<Bound_info> extremes,
- Drul_array<Offset> attachments,
- Real r_0, Real h_inf);
-static Direction get_default_dir (Grob *me);
-
-static void set_end_points (Grob *);
-static Real broken_trend_y (Grob *me, Grob **, Direction dir);
-static Drul_array<Bound_info> get_bound_info (Spanner *me, Grob **common);
-
-static void generate_curves (Grob *me,
- Grob *common[],Slur_score_parameters*,
- Drul_array<Bound_info> extremes,
- Drul_array<Offset> base_attach,
- Array<Slur_score> *scores);
-static Array<Slur_score> enumerate_attachments
-(Grob *me, Grob *common[], Slur_score_parameters*,
- Drul_array<Bound_info> extremes,
- Drul_array<Offset> base_attachment, Drul_array<Real> end_ys);
-static Drul_array<Offset> get_base_attachments
-(Spanner *sp, Grob **common, Drul_array<Bound_info> extremes);
-static Drul_array<Real> get_y_attachment_range
-(Spanner *sp, Grob **common,
- Slur_score_parameters*,
- Drul_array<Bound_info> extremes,
- Drul_array<Offset> base_attachment);
-
-
-Real
-get_detail (SCM alist, SCM sym)
-{
- SCM entry = scm_assq (sym, alist);
- return robust_scm2double (ly_c_pair_p (entry)
- ? ly_cdr (entry)
- : SCM_EOL,
- 0.0);
-}
-
-void
-init_score_param (Grob *me,
- Slur_score_parameters *score_param)
-{
- SCM details = me->get_property ("slur-details");
-
- score_param->region_size_
- = (int) get_detail (details, ly_symbol2scm ("region-size"));
- score_param->head_encompass_penalty_
- = get_detail (details, ly_symbol2scm ("head-encompass-penalty"));
- score_param->stem_encompass_penalty_
- = get_detail (details, ly_symbol2scm ("stem-encompass-penalty"));
- score_param->closeness_factor_
- = get_detail (details, ly_symbol2scm ("closeness-factor"));
- score_param->edge_attraction_factor_
- = get_detail (details, ly_symbol2scm ("edge-attraction-factor"));
- score_param->same_slope_penalty_
- = get_detail (details, ly_symbol2scm ("same-slope-penalty"));
- score_param->steeper_slope_factor_
- = get_detail (details, ly_symbol2scm ("steeper-slope-factor"));
- score_param->non_horizontal_penalty_
- = get_detail (details, ly_symbol2scm ("non-horizontal-penalty"));
- score_param->max_slope_
- = get_detail (details, ly_symbol2scm ("max-slope"));
- score_param->max_slope_factor_
- = get_detail (details, ly_symbol2scm ("max-slope-factor"));
- score_param->free_head_distance_
- = get_detail (details, ly_symbol2scm ("free-head-distance"));
- score_param->extra_object_collision_
- = get_detail (details, ly_symbol2scm ("extra-object-collision"));
- score_param->accidental_collision_
- = get_detail (details, ly_symbol2scm ("accidental-collision"));
- score_param->extra_encompass_free_distance_
- = get_detail (details, ly_symbol2scm ("extra-encompass-free-distance"));
-}
-
-
-Slur_score_parameters::Slur_score_parameters(Grob *me)
-{
- init_score_param (me, this);
-}
-
-/* HDIR indicates the direction for the slur. */
-Real
-broken_trend_y (Grob *me, Grob **common, Direction hdir)
-{
- /* A broken slur should maintain the same vertical trend
- the unbroken slur would have had. */
- Real by = 0.0;
- if (Spanner *mother = dynamic_cast<Spanner*> (me->original_))
- {
- int k = broken_spanner_index (dynamic_cast<Spanner*> (me));
- int j = k + hdir;
- if (j < 0 || j >= mother->broken_intos_.size ())
- return by;
-
- Grob *neighbor = mother->broken_intos_[j];
- if (hdir == RIGHT)
- neighbor->set_property ("direction",
- me->get_property ("direction"));
-
- Spanner *common_mother
- = dynamic_cast<Spanner*> (common[Y_AXIS]->original_);
- int common_k
- = broken_spanner_index (dynamic_cast<Spanner*> (common[Y_AXIS]));
- int common_j = common_k + hdir;
-
- if (common_j < 0 || common_j >= common_mother->broken_intos_.size())
- return by;
-
- Grob *common_next_system = common_mother->broken_intos_[common_j];
- Link_array<Grob> neighbor_cols
- = Pointer_group_interface__extract_grobs (neighbor, (Grob *)0,
- "note-columns");
-
- Grob *neighbor_col
- = (hdir == RIGHT) ? neighbor_cols[0] : neighbor_cols.top ();
- Grob *neighbor_common
- = common_next_system->common_refpoint (neighbor_col, Y_AXIS);
-
- 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))
- - common_next_system->relative_coordinate (neighbor_common, Y_AXIS);
-
- Link_array<Grob> my_cols
- = Pointer_group_interface__extract_grobs (me, (Grob *)0,
- "note-columns");
-
- Grob *extreme_col = (hdir == RIGHT) ? my_cols.top() : my_cols[0];
- Real y = extreme_col->extent (common[Y_AXIS], Y_AXIS)
- .linear_combination (int ((my_cols.size() == 1) ? CENTER : vdir));
- by = (y*neighbor_cols.size() + neighbor_y*my_cols.size()) /
- (my_cols.size() + neighbor_cols.size());
- }
- return by;
-}
-
-Encompass_info
-get_encompass_info (Grob *me,
- Grob *col,
- Grob **common)
-{
- Grob *stem = unsmob_grob (col->get_property ("stem"));
- Encompass_info ei;
- Direction dir = get_grob_direction (me);
-
- if (!stem)
- {
- programming_error ("No stem for note column?");
- ei.x_ = col->relative_coordinate (common[X_AXIS], X_AXIS);
- ei.head_ = ei.stem_ = col->extent (common[Y_AXIS],
- Y_AXIS)[get_grob_direction (me)];
- return ei;
- }
- Direction stem_dir = get_grob_direction (stem);
-
- if (Grob *head = Note_column::first_head (col))
- ei.x_ = head->extent (common[X_AXIS], X_AXIS).center ();
- else
- ei.x_ = col->extent (common[X_AXIS], X_AXIS).center ();
-
- Grob *h = Stem::extremal_heads (stem)[Direction (dir)];
- if (!h)
- {
- ei.head_ = ei.stem_ = col->extent (common[Y_AXIS], Y_AXIS)[dir];
- return ei;
- }
-
- ei.head_ = h->extent (common[Y_AXIS], Y_AXIS)[dir];
-
- if ((stem_dir == dir)
- && !stem->extent (stem, Y_AXIS).is_empty ())
- {
- ei.stem_ = stem->extent (common[Y_AXIS], Y_AXIS)[dir];
- if (Grob *b = Stem::get_beam (stem))
- ei.stem_ += stem_dir * 0.5 * Beam::get_thickness (b);
-
- Interval x = stem->extent (common[X_AXIS], X_AXIS);
- ei.x_ = x.is_empty ()
- ? stem->relative_coordinate (common[X_AXIS], X_AXIS)
- : x.center ();
- }
- else
- ei.stem_ = ei.head_;
-
- return ei;
-}
-
-
-Direction
-get_default_dir (Grob*me)
-{
- Link_array<Grob> encompasses
- = Pointer_group_interface__extract_grobs (me, (Grob*) 0, "note-columns");
-
- Direction d = DOWN;
- for (int i= 0; i < encompasses.size (); i ++)
- {
- if (Note_column::dir (encompasses[i]) < 0)
- {
- d = UP;
- break;
- }
- }
- return d;
-}
-
-Direction
-get_slur_dir (Grob *slur)
-{
- Direction d = get_grob_direction (slur);
- if (d == CENTER)
- d = get_default_dir (slur);
- return d;
-}
-
-MAKE_SCHEME_CALLBACK (New_slur, after_line_breaking,1);
-SCM
-New_slur::after_line_breaking (SCM smob)
-{
- Spanner *me = dynamic_cast<Spanner*> (unsmob_grob (smob));
- if (!scm_ilength (me->get_property ("note-columns")))
- {
- me->suicide ();
- return SCM_UNSPECIFIED;
- }
-
- if (!get_grob_direction (me))
- set_grob_direction (me, get_default_dir (me));
-
- if (scm_ilength (me->get_property ("control-points")) < 4)
- set_end_points (me);
-
- return SCM_UNSPECIFIED;
-}
-
-Drul_array<Bound_info>
-get_bound_info (Spanner* me, Grob **common)
-{
- Drul_array<Bound_info> extremes;
- Direction d = LEFT;
- Direction dir = get_grob_direction (me);
-
- do
- {
- extremes[d].bound_ = me->get_bound (d);
- if (Note_column::has_interface (extremes[d].bound_))
- {
- extremes[d].note_column_ = extremes[d].bound_;
- extremes[d].stem_ = Note_column::get_stem (extremes[d].note_column_);
- extremes[d].stem_dir_ = get_grob_direction (extremes[d].stem_);
- extremes[d].stem_extent_[X_AXIS]
- = extremes[d].stem_->extent (common[X_AXIS], X_AXIS);
- extremes[d].stem_extent_[Y_AXIS]
- = extremes[d].stem_->extent (common[Y_AXIS], Y_AXIS);
- extremes[d].slur_head_
- = Stem::extremal_heads (extremes[d].stem_)[dir];
- if (!extremes[d].slur_head_
- && Note_column::has_rests (extremes[d].bound_))
- {
- extremes[d].slur_head_ = Note_column::get_rest (extremes[d].bound_);
- }
-
- if (extremes[d].slur_head_)
- extremes[d].slur_head_extent_
- = extremes[d].slur_head_->extent (common[X_AXIS], X_AXIS);
-
- extremes[d].staff_ = Staff_symbol_referencer
- ::get_staff_symbol (extremes[d].stem_);
- extremes[d].staff_space_ = Staff_symbol_referencer
- ::staff_space (extremes[d].stem_);
- }
- else
- extremes[d].neighbor_y_ = broken_trend_y (me, common, d);
- }
- while (flip (&d) != LEFT);
- return extremes;
-}
-
-void
-set_end_points (Grob *me)
-{
-
-
-
- Link_array<Grob> columns
- = Pointer_group_interface__extract_grobs (me, (Grob *) 0, "note-columns");
-
- if (columns.is_empty ())
- {
- me->suicide ();
- return;
- }
-
- SCM eltlist = me->get_property ("note-columns");
- SCM extra_list = me->get_property ("encompass-objects");
- Spanner *sp = dynamic_cast<Spanner*> (me);
-
- Grob *common[] = {0, 0};
- for (int i = X_AXIS; i < NO_AXES; i++)
- {
- Axis a = (Axis)i;
- common[a] = common_refpoint_of_list (eltlist, me, a);
- common[a] = common_refpoint_of_list (extra_list, common[a], a);
- }
-
- common[X_AXIS] = common[X_AXIS]->common_refpoint (sp->get_bound (RIGHT),
- X_AXIS);
- common[X_AXIS] = common[X_AXIS]->common_refpoint (sp->get_bound (LEFT),
- X_AXIS);
-
- Drul_array<Bound_info> extremes = get_bound_info (sp, common);
- Drul_array<Offset> base_attachment
- = get_base_attachments (sp, common, extremes);
- Slur_score_parameters params (me);
- Drul_array<Real> end_ys
- = get_y_attachment_range (sp, common, ¶ms, extremes, base_attachment);
- Array<Slur_score> scores = enumerate_attachments (me, common, ¶ms,
- extremes, base_attachment,
- end_ys);
-
- generate_curves (me, common, ¶ms, extremes, base_attachment, &scores);
- score_edges (me, common, ¶ms,extremes, base_attachment, &scores);
- score_slopes (me, common, ¶ms,extremes, base_attachment, &scores);
- score_encompass (me, common, ¶ms,extremes, base_attachment, &scores);
- score_extra_encompass (me, common, ¶ms,extremes, base_attachment,
- &scores);
-
- Real opt = 1e6;
- int opt_idx = 0;
- // why backwards?
- for (int i = scores.size (); i--;)
- {
- if (scores[i].score_ < opt)
- {
- opt = scores[i].score_;
- opt_idx = i;
- }
- }
-
-#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")))
- && ly_c_pair_p (inspect_quants))
- {
- Drul_array<Real> ins = ly_scm2interval (inspect_quants);
- Real mindist = 1e6;
- for (int i = 0; i < scores.size (); i ++)
- {
- Real d =fabs (scores[i].attachment_[LEFT][Y_AXIS] - ins[LEFT])
- + fabs (scores[i].attachment_[RIGHT][Y_AXIS] - ins[RIGHT]);
- if (d < mindist)
- {
- opt_idx = i;
- mindist= d;
- }
- }
- if (mindist > 1e5)
- programming_error ("Could not find quant.");
- }
- scores[opt_idx].score_card_ += to_string ("i%d", opt_idx);
-
- // debug quanting
- me->set_property ("quant-score",
- scm_makfrom0str (scores[opt_idx].score_card_.to_str0 ()));
-#endif
-
- Bezier b = scores[opt_idx].curve_;
- SCM controls = SCM_EOL;
- for (int i = 4; i--;)
- {
- Offset o = b.control_[i]
- - Offset (me->relative_coordinate (common[X_AXIS], X_AXIS),
- me->relative_coordinate (common[Y_AXIS], Y_AXIS));
- controls = scm_cons (ly_offset2scm (o), controls);
- }
- me->set_property ("control-points", controls);
-}
-
-/*
- TODO: should analyse encompasses to determine sensible region, and
- should limit slopes available.
- */
-
-Drul_array<Real>
-get_y_attachment_range (Spanner*me,
- Grob **common, Slur_score_parameters *score_param,
- Drul_array<Bound_info> extremes,
- Drul_array<Offset> base_attachment)
-{
- Drul_array<Real> end_ys;
- Direction dir = get_grob_direction (me);
- Direction d = LEFT;
- do
- {
- if (extremes[d].note_column_)
- {
- end_ys[d] = dir
- * ((dir * (base_attachment[d][Y_AXIS] + score_param->region_size_* dir))
- >? (dir * (dir + extremes[d].note_column_->extent(common[Y_AXIS],
- Y_AXIS)[dir]))
- >? (dir * base_attachment[-d][Y_AXIS]));
- }
- else
- end_ys[d] = extremes[d].neighbor_y_ + score_param->region_size_ * dir;
- }
- while (flip (&d) != LEFT);
-
- return end_ys;
-}
-
-bool
-spanner_less (Spanner *s1, Spanner* s2)
-{
- Slice b1, b2;
- Direction d = LEFT;
- do
- {
- b1[d] = s1->get_bound (d)->get_column ()->rank_;
- b2[d] = s2->get_bound (d)->get_column ()->rank_;
- } while (flip (&d) != LEFT);
-
- return b2[LEFT] <= b1[LEFT] && b2[RIGHT] >= b1[RIGHT]
- && (b2[LEFT] != b1[LEFT] || b2[RIGHT] != b1[RIGHT]);
-}
-
-
-Drul_array<Offset>
-get_base_attachments (Spanner *me,
- Grob **common, Drul_array<Bound_info> extremes)
-{
- Link_array<Grob> columns
- = Pointer_group_interface__extract_grobs (me, (Grob *)0, "note-columns");
- Drul_array<Offset> base_attachment;
- Real staff_space = Staff_symbol_referencer::staff_space ((Grob *) me);
- Direction dir = get_grob_direction (me);
- Direction d = LEFT;
- do
- {
- Grob *stem = extremes[d].stem_;
- Grob *head = extremes[d].slur_head_;
-
- 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];
- else
- x = me->get_broken_left_end_align ();
- }
- else
- {
- bool same_beam =
- (extremes[d].stem_ && extremes[-d].stem_
- && Stem::get_beam (extremes[d].stem_) == Stem::get_beam (extremes[-d].stem_));
-
- /*
- fixme: X coord should also be set in this case.
- */
- if (stem
- && extremes[d].stem_dir_ == dir
- && Stem::get_beaming (stem, -d)
- && (!spanner_less (me, Stem::get_beam (stem))
- || same_beam))
- y = extremes[d].stem_extent_[Y_AXIS][dir];
- else if (head)
- y = head->extent (common[Y_AXIS], Y_AXIS)[dir];
- y += dir * 0.5 * staff_space;
-
- Real pos
- = (y - extremes[d].staff_->relative_coordinate (common[Y_AXIS],
- Y_AXIS))
- * 2.0 / Staff_symbol::staff_space (extremes[d].staff_);
-
- /* start off staffline. */
- if (fabs (pos - my_round (pos)) < 0.2
- && Staff_symbol_referencer::on_staffline (head, (int) rint (pos))
- && Staff_symbol_referencer::line_count (head) - 1 >= rint (pos)
- )
- // TODO: calc from slur thick & line thick, parameter.
- y += 1.5 * staff_space * dir / 10;
-
- Grob * fh = Note_column::first_head (extremes[d].note_column_);
- x =
- (fh ? fh->extent (common[X_AXIS], X_AXIS)
- : extremes[d].bound_->extent (common[X_AXIS], X_AXIS))
- .linear_combination (CENTER);
- }
- base_attachment[d] = Offset (x, y);
-
- } while (flip (&d) != LEFT);
-
- return base_attachment;
-}
-
-void
-generate_curves (Grob *me, Grob **common,
- Slur_score_parameters *score_param,
- Drul_array<Bound_info> extremes,
- Drul_array<Offset>,
- Array<Slur_score> *scores)
-{
- (void) common;
- (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"));
- for (int i = 0; i < scores->size(); i++)
- {
- 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];
- (*scores)[i].curve_ = bez;
- }
-}
-
-Bezier
-avoid_staff_line (Grob *me, Grob **common,
- Drul_array<Bound_info> extremes,
- Bezier bez)
-{
- Offset horiz (1,0);
- Array<Real> ts = bez.solve_derivative (horiz);
- Real lt = me->get_paper ()->get_dimension (ly_symbol2scm ("linethickness"));
- Real thick = robust_scm2double (me->get_property ("thickness"), 1.0) * lt;
-
- /* TODO: handle case of broken slur. */
- if (!ts.is_empty ()
- && (extremes[LEFT].staff_ == extremes[RIGHT].staff_)
- && extremes[LEFT].staff_ && extremes[RIGHT].staff_)
- {
- Real y = bez.curve_point (ts[0])[Y_AXIS];
-
- Grob *staff = extremes[LEFT].staff_;
-
- Real staff_space = extremes[LEFT].staff_space_;
- Real p = 2 * (y - staff->relative_coordinate (common[Y_AXIS], Y_AXIS))
- / staff_space;
-
- Real distance = fabs (my_round (p) - p); // in halfspaces
- if (distance < 4 * thick
- && (int) fabs (my_round (p))
- <= 2 * Staff_symbol_referencer::staff_radius (staff) + 0.1
- && (int (fabs (my_round (p))) % 2
- != Staff_symbol_referencer::line_count (staff) % 2))
- {
- Direction resolution_dir =
- (distance ? get_grob_direction (me) : Direction (sign (p - my_round(p))));
-
- // TODO: parameter
- Real newp = my_round (p) + resolution_dir
- * 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;
-}
-
-Array<Slur_score>
-enumerate_attachments (Grob *me, Grob *common[],
- Slur_score_parameters *score_param,
- Drul_array<Bound_info> extremes,
- Drul_array<Offset> base_attachment,
- Drul_array<Real> end_ys)
-{
- (void) common;
- /*ugh. */
- Array<Slur_score> scores;
-
- Direction dir = get_grob_direction (me);
- Real staff_space = Staff_symbol_referencer::staff_space ((Grob *) me);
-
- Drul_array<Offset> os;
- os[LEFT] = base_attachment[LEFT];
- Real minimum_length = staff_space
- * robust_scm2double (me->get_property ("minimum-length"), 2.0);
-
- for (int i = 0; dir * os[LEFT][Y_AXIS] <= dir * end_ys[LEFT]; i++)
- {
- os[RIGHT] = base_attachment[RIGHT];
- for (int j = 0; dir * os[RIGHT][Y_AXIS] <= dir * end_ys[RIGHT]; j++)
- {
- Slur_score s;
- Direction d = LEFT;
- Drul_array<bool> attach_to_stem (false, false);
- do
- {
- os[d][X_AXIS] = base_attachment[d][X_AXIS];
- if (extremes[d].stem_
- && !Stem::is_invisible (extremes[d].stem_)
- && extremes[d].stem_dir_ == dir)
- {
- if (dir == -d
- && extremes[d].stem_extent_[Y_AXIS].contains (os[d][Y_AXIS]))
- {
- os[d][X_AXIS] = extremes[d].slur_head_extent_[-d]
- - d * 0.3;
- attach_to_stem[d] = true;
- }
- else if (dir *extremes[d].stem_extent_[Y_AXIS][dir]
- < dir * os[d][Y_AXIS]
- && !extremes[d].stem_extent_[X_AXIS].is_empty()
- )
-
- os[d][X_AXIS] = extremes[d].stem_extent_[X_AXIS].center();
- }
- }
- while (flip (&d) != LEFT);
-
- Offset dz;
- dz = os[RIGHT] - os[LEFT];
- if (dz[X_AXIS] < minimum_length
- || fabs (dz[Y_AXIS] / dz[X_AXIS]) > score_param->max_slope_
- )
- {
- do
- {
- if (extremes[d].slur_head_)
- {
- os[d][X_AXIS] = extremes[d].slur_head_extent_.center ();
- attach_to_stem[d] = false;
- }
- }
- while (flip (&d) != LEFT);
- }
-
- dz = os[RIGHT] - os[LEFT];
- do
- {
- if (extremes[d].slur_head_
- && !attach_to_stem[d])
- {
- /* Horizontally move tilted slurs a little. Move
- more for bigger tilts.
-
- TODO: parameter */
- os[d][X_AXIS]
- -= dir * extremes[d].slur_head_extent_.length ()
- * sin (dz.arg ()) / 3;
- }
- }
- while (flip (&d) != LEFT);
-
- s.attachment_ = os;
- scores.push (s);
-
- os[RIGHT][Y_AXIS] += dir * staff_space / 2;
- }
-
- os[LEFT][Y_AXIS] += dir * staff_space / 2;
- }
- return scores;
-}
-
-void
-score_encompass (Grob *me, Grob *common[],
- Slur_score_parameters *score_param,
- Drul_array<Bound_info> extremes,
- Drul_array<Offset> base_attach,
- Array<Slur_score> *scores)
-{
- (void) extremes;
- (void) base_attach;
-
- Link_array<Grob> encompasses
- = Pointer_group_interface__extract_grobs (me, (Grob *)0, "note-columns");
- Direction dir = get_grob_direction (me);
-
- Array<Encompass_info> infos;
-
- for (int i = 0; i < encompasses.size(); i++)
- infos.push (get_encompass_info (me, encompasses[i], common));
-
- for (int i = 0; i < scores->size (); i++)
- {
- Bezier const &bez (scores->elem (i).curve_);
- Real demerit = 0.0;
- for (int j = 0; j < infos.size(); j++)
- {
- Real x = infos[j].x_;
-
- bool l_edge = j==0;
- 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]))
- continue;
-
- Real y = bez.get_other_coordinate (X_AXIS, x);
- if (!edge)
- {
- Real head_dy = (y - infos[j].head_);
- if (dir * head_dy < 0)
- demerit += score_param->head_encompass_penalty_;
- else
- {
- Real hd = (head_dy)
- ? (1 / fabs (head_dy) - 1 / score_param->free_head_distance_)
- : score_param->head_encompass_penalty_;
- hd = (hd >? 0)<? score_param->head_encompass_penalty_;
-
- demerit += hd;
- }
- }
-
- if (dir * (y - infos[j].stem_) < 0)
- {
- Real stem_dem =score_param->stem_encompass_penalty_ ;
- if ((l_edge && dir == UP)
- || (r_edge && dir == DOWN))
- stem_dem /= 5;
-
- demerit += stem_dem;
- }
- else if (!edge)
- {
- Interval ext;
- ext.add_point (infos[j].stem_);
- ext.add_point (infos[j].head_);
-
- // ?
- demerit += -score_param->closeness_factor_
- * (dir
- * (y - (ext[dir] + dir * score_param->free_head_distance_))
- <? 0)
- / infos.size ();
- }
- }
-
-#if DEBUG_SLUR_QUANTING
- (*scores)[i].score_card_ += to_string ("C%.2f", demerit);
-#endif
-
- (*scores)[i].score_ += demerit;
- }
-}
-
-void
-score_extra_encompass (Grob *me, Grob *common[],
- Slur_score_parameters *score_param,
- Drul_array<Bound_info> extremes,
- Drul_array<Offset> base_attach,
- Array<Slur_score> *scores)
-{
- (void) base_attach;
- (void) extremes;
-
- Link_array<Grob> encompasses
- = Pointer_group_interface__extract_grobs (me, (Grob *)0,
- "encompass-objects");
- Direction dir = get_grob_direction (me);
- 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;
-
- for (int i = encompasses.size (); i--; )
- {
- if (New_slur::has_interface (encompasses[i]))
- {
- Grob * small_slur = encompasses[i];
- Bezier b = New_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));
- }
- else
- {
- Grob *g = encompasses [i];
- Interval xe = g->extent (common[X_AXIS], X_AXIS);
- Interval ye = g->extent (common[Y_AXIS], Y_AXIS);
-
- Real xp = 0.0;
-
- if (Accidental_interface::has_interface (g))
- {
- /* Begin copy accidental.cc */
- bool parens = false;
- if (to_boolean (g->get_property ("cautionary")))
- {
- SCM cstyle = g->get_property ("cautionary-style");
- parens = ly_c_equal_p (cstyle, ly_symbol2scm ("parentheses"));
- }
-
- SCM accs = g->get_property ("accidentals");
- SCM scm_style = g->get_property ("style");
- if (!ly_c_symbol_p (scm_style)
- && !parens
- && scm_ilength (accs) == 1)
- {
- /* End copy accidental.cc */
- switch (ly_scm2int (ly_car (accs)))
- {
- case FLAT:
- case DOUBLE_FLAT:
- xp = LEFT;
- break ;
- case SHARP:
- xp = 0.5 * dir;
- break ;
- case NATURAL:
- xp = -dir;
- break;
- }
- }
- }
-
- xidxs.push (xp);
- ye.widen (thick * 0.5);
- yexts.push (ye);
- xexts.push (xe);
- }
- }
- for (int i = 0; i < scores->size (); i++)
- {
- Real demerit = 0.0;
- for (int j = 0; j < xidxs.size(); j++)
- {
- Drul_array<Offset> at = scores->elem (i).attachment_;
- Interval slur_wid (at[LEFT][X_AXIS], at[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]);
- Real y = 0.0;
- if (!slur_wid.contains (x))
- {
- Direction contains_dir = CENTER;
- Direction d = LEFT;
- do
- {
- if (xexts[j].contains (at[d][X_AXIS]))
- contains_dir = d;
- }
- while (flip (&d) != LEFT);
-
- if (!contains_dir)
- continue;
- else
- y = at[contains_dir][Y_AXIS];
- }
- else
- {
- 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);
- demerit +=
- fabs (0 >? (score_param->extra_encompass_free_distance_ - dist)) /
- score_param->extra_encompass_free_distance_ * collision_demerit;
- }
-#if DEBUG_SLUR_QUANTING
- (*scores)[i].score_card_ += to_string ("X%.2f", demerit);
-#endif
- (*scores)[i].score_ += demerit;
- }
-}
-
-void
-score_edges (Grob *me, Grob *common[],
- Slur_score_parameters * score_param,
- Drul_array<Bound_info> extremes,
- Drul_array<Offset> base_attach,
- Array<Slur_score> *scores)
-{
- (void) common;
- Direction dir = get_grob_direction (me);
-
- for (int i = 0; i < scores->size (); i++)
- {
- Direction d = LEFT;
- do
- {
- Real y = scores->elem (i).attachment_[d][Y_AXIS];
- Real dy = fabs (y - base_attach[d][Y_AXIS]);
-
- Real factor = score_param->edge_attraction_factor_;
- Real demerit = factor * dy;
- if (extremes[d].stem_
- && extremes[d].stem_dir_ == dir
- && !Stem::get_beaming (extremes[d].stem_, -d)
- )
- demerit /= 5;
-
- (*scores)[i].score_ += demerit;
-#if DEBUG_SLUR_QUANTING
- (*scores)[i].score_card_ += to_string ("E%.2f", demerit);
-#endif
- }
- while (flip (&d) != LEFT);
- }
-}
-
-void
-score_slopes (Grob *me, Grob *common[],
- Slur_score_parameters *score_param,
- Drul_array<Bound_info> extremes,
- Drul_array<Offset> base_attach,
- Array<Slur_score> * scores)
-{
- (void) me;
- (void) base_attach;
-
- Drul_array<Real> ys;
- Direction d = LEFT;
- do
- {
- if (extremes[d].slur_head_)
- ys[d] = extremes[d].slur_head_->relative_coordinate (common[Y_AXIS],
- Y_AXIS);
- else
- ys[d] = extremes[d].neighbor_y_;
- }
- while (flip (&d) != LEFT);
-
- bool has_beams
- = (extremes[LEFT].stem_ && Stem::get_beam (extremes[LEFT].stem_))
- || (extremes[RIGHT].stem_ && Stem::get_beam (extremes[RIGHT].stem_));
-
- Real dy = ys[RIGHT] - ys[LEFT];
- for (int i = 0; i < scores->size (); i++)
- {
- Offset slur_dz = (*scores)[i].attachment_[RIGHT]
- - (*scores)[i].attachment_[LEFT];
- Real slur_dy = slur_dz[Y_AXIS];
- Real demerit = 0.0;
-
- demerit += ((fabs (slur_dy / slur_dz[X_AXIS])
- - score_param->max_slope_) >? 0)
- * score_param->max_slope_factor_;
-
- /* 0.2: account for staffline offset. */
- Real max_dy = (fabs (dy) + 0.2);
- if (has_beams)
- max_dy += 1.0;
-
- demerit += score_param->steeper_slope_factor_
- * ((fabs (slur_dy) -max_dy) >? 0);
-
- demerit += ((fabs (slur_dy/slur_dz[X_AXIS])
- - score_param->max_slope_) >? 0)
- * score_param->max_slope_factor_;
-
- if (sign (dy) == 0
- && sign (slur_dy) != 0)
- demerit += score_param->non_horizontal_penalty_;
-
- if (sign (dy)
- && sign (slur_dy)
- && sign (slur_dy) != sign (dy))
- demerit += has_beams
- ? score_param->same_slope_penalty_ / 10
- : score_param->same_slope_penalty_;
-
-#if DEBUG_SLUR_QUANTING
- (*scores)[i].score_card_ += to_string ("S%.2f", d);
-#endif
- (*scores)[i].score_ += demerit;
- }
-
-
-
-}
-
-
-Real
-fit_factor (Offset dz_unit, Offset dz_perp,
- Bezier curve, Direction d, Array<Offset> const &avoid)
-{
- Real fit_factor = 0.0;
- Offset x0 = curve.control_[0];
- curve.translate (-x0);
- curve.rotate (-dz_unit.arg ());
- curve.scale (1, d);
-
- Interval curve_xext;
- curve_xext.add_point (curve.control_[0][X_AXIS]);
- curve_xext.add_point (curve.control_[3][X_AXIS]);
-
- for (int i = 0; i < avoid.size (); i++)
- {
- Offset z = (avoid[i] - x0) ;
- Offset p (dot_product (z, dz_unit),
- d* dot_product (z, dz_perp));
- if (!curve_xext.contains (p[X_AXIS]))
- continue;
-
- Real y = curve.get_other_coordinate (X_AXIS, p[X_AXIS]);
- if (y)
- {
- fit_factor = fit_factor >? (p[Y_AXIS] / y);
- }
- }
- return fit_factor;
-}
-
-
-Bezier
-get_bezier (Grob *me,
- Grob **common,
- Slur_score_parameters *score_param,
- Drul_array<Bound_info> extremes,
- Drul_array<Offset> attachments,
- 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;
- for (int i = 0; i < encompasses.size(); i++)
- {
- if (extremes[LEFT].note_column_ == encompasses[i]
- ||extremes[RIGHT].note_column_ == encompasses[i])
- continue;
-
- Encompass_info inf (get_encompass_info (me, encompasses[i], common));
-
- Real y = dir*((dir * inf.head_) >? (dir *inf.stem_));
-
- avoid.push (Offset (inf.x_, y + dir * score_param->free_head_distance_));
- }
-
- Offset dz = attachments[RIGHT]- attachments[LEFT];;
- Offset dz_unit = dz;
- dz_unit *= 1 / dz.length();
- Offset dz_perp = dz_unit * Offset(0,1);
-
- Real indent, height;
- get_slur_indent_height (&indent, &height, dz.length (), h_inf, r_0);
-
- Real excentricity = robust_scm2double (me->get_property ("excentricity"), 0.0);
- Bezier curve;
-
- Real x1 = (excentricity + indent);
- Real x2 = (excentricity - indent);
- curve.control_[0] = attachments[LEFT];
- curve.control_[1] = attachments[LEFT] + dz_perp * height * dir + dz_unit * x1;
- curve.control_[2] = attachments[RIGHT] + dz_perp * height * dir + dz_unit * x2;
- curve.control_[3] = attachments[RIGHT];
-
-
- Real ff = fit_factor (dz_unit, dz_perp, curve, dir, avoid);
- Real l = dz.length ();
-
- /*
- This condition,
-
- l^2 > 4h^2 + 3(i 1/3l)^2 - 1/3 l^2
-
- is equivalent to:
-
- |bez'(0)| < | bez'(.5)|
-
- when (control2-control1) has the same direction as (control3 -
- control0).
-
- */
- Real max_h = sqrt (sqr (l)/3 - .75 * sqr (indent + l / 3));
- height = height >? ((height * ff) <? max_h);
-
- curve.control_[0] = attachments[LEFT];
- curve.control_[1] = attachments[LEFT] + dz_perp * height * dir + dz_unit * x1;
- curve.control_[2] = attachments[RIGHT] + dz_perp * height * dir + dz_unit * x2;
- curve.control_[3] = attachments[RIGHT];
-
- return curve;
-}
--- /dev/null
+/*
+ slur-quanting.cc -- Score based slur formatting
+
+ source file of the GNU LilyPond music typesetter
+
+ (c) 1996--2004 Han-Wen Nienhuys <hanwen@cs.uu.nl>
+ Jan Nieuwenhuizen <janneke@gnu.org>
+*/
+
+#include <math.h>
+
+#include "accidental-interface.hh"
+#include "beam.hh"
+#include "directional-element-interface.hh"
+#include "group-interface.hh"
+#include "libc-extension.hh"
+#include "lily-guile.hh"
+#include "new-slur.hh"
+#include "note-column.hh"
+#include "output-def.hh"
+#include "pitch.hh"
+#include "bezier.hh"
+#include "spanner.hh"
+#include "staff-symbol-referencer.hh"
+#include "staff-symbol.hh"
+#include "stem.hh"
+#include "warn.hh"
+#include "paper-column.hh"
+
+
+struct Slur_score
+{
+ Drul_array<Offset> attachment_;
+ Real score_;
+ Bezier curve_;
+
+#if DEBUG_SLUR_QUANTING
+ String score_card_;
+#endif
+
+ Slur_score()
+ {
+ score_ = 0.0;
+ }
+};
+
+/*
+ TODO: put in details property.,
+
+ use lowercase.
+*/
+struct Slur_score_parameters
+{
+ int region_size_;
+ Real head_encompass_penalty_;
+ Real stem_encompass_penalty_;
+ Real closeness_factor_;
+ Real edge_attraction_factor_;
+ Real same_slope_penalty_;
+ Real steeper_slope_factor_;
+ Real non_horizontal_penalty_;
+ Real max_slope_;
+ Real max_slope_factor_;
+ Real extra_object_collision_;
+ Real accidental_collision_;
+ Real free_head_distance_;
+ Real extra_encompass_free_distance_;
+ 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_;
+ Real stem_;
+ Real head_;
+ Encompass_info ()
+ {
+ x_ = 0.0;
+ stem_ = 0.0;
+ head_ = 0.0;
+ }
+};
+
+struct Bound_info
+{
+ Box stem_extent_;
+ Direction stem_dir_;
+ Item *bound_;
+ Grob *note_column_;
+ Grob *slur_head_;
+ Grob *staff_;
+ Grob *stem_;
+ Interval slur_head_extent_;
+ Real neighbor_y_;
+ Real staff_space_;
+
+ Bound_info ()
+ {
+ stem_ = 0;
+ neighbor_y_ = 0;
+ staff_ = 0;
+ slur_head_ = 0;
+ stem_dir_ = CENTER;
+ note_column_ = 0;
+ }
+};
+
+/*
+ TODO: create one object for passing all parameters.
+ */
+
+
+static void
+score_extra_encompass (Grob *me, Grob *common[],
+ Slur_score_parameters *score_param,
+ Drul_array<Bound_info> ,
+ Drul_array<Offset> ,
+ Array<Slur_score> * scores);
+static void score_slopes (Grob *me, Grob *common[],
+ Slur_score_parameters *score_param,
+ Drul_array<Bound_info>,
+ Drul_array<Offset> base_attach,
+ Array<Slur_score> *scores);
+
+static void score_edges (Grob *me, Grob *common[],
+ Slur_score_parameters *score_param,
+ Drul_array<Bound_info> extremes,
+ Drul_array<Offset> base_attach,
+ Array<Slur_score> *scores);
+static void score_encompass (Grob *me, Grob *common[],
+ Slur_score_parameters*,
+ Drul_array<Bound_info>,
+ Drul_array<Offset>, Array<Slur_score> *scores);
+static Bezier avoid_staff_line (Grob *me, Grob **common,
+ Drul_array<Bound_info> extremes,
+ Bezier bez);
+
+static Encompass_info get_encompass_info (Grob *me,
+ Grob *col,
+ Grob **common);
+static Bezier get_bezier (Grob *me,
+ Grob **common,
+ Slur_score_parameters*,
+ Drul_array<Bound_info> extremes,
+ Drul_array<Offset> attachments,
+ Real r_0, Real h_inf);
+static Direction get_default_dir (Grob *me);
+
+static void set_end_points (Grob *);
+static Real broken_trend_y (Grob *me, Grob **, Direction dir);
+static Drul_array<Bound_info> get_bound_info (Spanner *me, Grob **common);
+
+static void generate_curves (Grob *me,
+ Grob *common[],Slur_score_parameters*,
+ Drul_array<Bound_info> extremes,
+ Drul_array<Offset> base_attach,
+ Array<Slur_score> *scores);
+static Array<Slur_score> enumerate_attachments
+(Grob *me, Grob *common[], Slur_score_parameters*,
+ Drul_array<Bound_info> extremes,
+ Drul_array<Offset> base_attachment, Drul_array<Real> end_ys);
+static Drul_array<Offset> get_base_attachments
+(Spanner *sp, Grob **common, Drul_array<Bound_info> extremes);
+static Drul_array<Real> get_y_attachment_range
+(Spanner *sp, Grob **common,
+ Slur_score_parameters*,
+ Drul_array<Bound_info> extremes,
+ Drul_array<Offset> base_attachment);
+
+
+Real
+get_detail (SCM alist, SCM sym)
+{
+ SCM entry = scm_assq (sym, alist);
+ return robust_scm2double (ly_c_pair_p (entry)
+ ? ly_cdr (entry)
+ : SCM_EOL,
+ 0.0);
+}
+
+void
+init_score_param (Grob *me,
+ Slur_score_parameters *score_param)
+{
+ SCM details = me->get_property ("slur-details");
+
+ score_param->region_size_
+ = (int) get_detail (details, ly_symbol2scm ("region-size"));
+ score_param->head_encompass_penalty_
+ = get_detail (details, ly_symbol2scm ("head-encompass-penalty"));
+ score_param->stem_encompass_penalty_
+ = get_detail (details, ly_symbol2scm ("stem-encompass-penalty"));
+ score_param->closeness_factor_
+ = get_detail (details, ly_symbol2scm ("closeness-factor"));
+ score_param->edge_attraction_factor_
+ = get_detail (details, ly_symbol2scm ("edge-attraction-factor"));
+ score_param->same_slope_penalty_
+ = get_detail (details, ly_symbol2scm ("same-slope-penalty"));
+ score_param->steeper_slope_factor_
+ = get_detail (details, ly_symbol2scm ("steeper-slope-factor"));
+ score_param->non_horizontal_penalty_
+ = get_detail (details, ly_symbol2scm ("non-horizontal-penalty"));
+ score_param->max_slope_
+ = get_detail (details, ly_symbol2scm ("max-slope"));
+ score_param->max_slope_factor_
+ = get_detail (details, ly_symbol2scm ("max-slope-factor"));
+ score_param->free_head_distance_
+ = get_detail (details, ly_symbol2scm ("free-head-distance"));
+ score_param->extra_object_collision_
+ = get_detail (details, ly_symbol2scm ("extra-object-collision"));
+ score_param->accidental_collision_
+ = get_detail (details, ly_symbol2scm ("accidental-collision"));
+ score_param->extra_encompass_free_distance_
+ = get_detail (details, ly_symbol2scm ("extra-encompass-free-distance"));
+}
+
+
+Slur_score_parameters::Slur_score_parameters(Grob *me)
+{
+ init_score_param (me, this);
+}
+
+/* HDIR indicates the direction for the slur. */
+Real
+broken_trend_y (Grob *me, Grob **common, Direction hdir)
+{
+ /* A broken slur should maintain the same vertical trend
+ the unbroken slur would have had. */
+ Real by = 0.0;
+ if (Spanner *mother = dynamic_cast<Spanner*> (me->original_))
+ {
+ int k = broken_spanner_index (dynamic_cast<Spanner*> (me));
+ int j = k + hdir;
+ if (j < 0 || j >= mother->broken_intos_.size ())
+ return by;
+
+ Grob *neighbor = mother->broken_intos_[j];
+ if (hdir == RIGHT)
+ neighbor->set_property ("direction",
+ me->get_property ("direction"));
+
+ Spanner *common_mother
+ = dynamic_cast<Spanner*> (common[Y_AXIS]->original_);
+ int common_k
+ = broken_spanner_index (dynamic_cast<Spanner*> (common[Y_AXIS]));
+ int common_j = common_k + hdir;
+
+ if (common_j < 0 || common_j >= common_mother->broken_intos_.size())
+ return by;
+
+ Grob *common_next_system = common_mother->broken_intos_[common_j];
+ Link_array<Grob> neighbor_cols
+ = Pointer_group_interface__extract_grobs (neighbor, (Grob *)0,
+ "note-columns");
+
+ Grob *neighbor_col
+ = (hdir == RIGHT) ? neighbor_cols[0] : neighbor_cols.top ();
+ Grob *neighbor_common
+ = common_next_system->common_refpoint (neighbor_col, Y_AXIS);
+
+ 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))
+ - common_next_system->relative_coordinate (neighbor_common, Y_AXIS);
+
+ Link_array<Grob> my_cols
+ = Pointer_group_interface__extract_grobs (me, (Grob *)0,
+ "note-columns");
+
+ Grob *extreme_col = (hdir == RIGHT) ? my_cols.top() : my_cols[0];
+ Real y = extreme_col->extent (common[Y_AXIS], Y_AXIS)
+ .linear_combination (int ((my_cols.size() == 1) ? CENTER : vdir));
+ by = (y*neighbor_cols.size() + neighbor_y*my_cols.size()) /
+ (my_cols.size() + neighbor_cols.size());
+ }
+ return by;
+}
+
+Encompass_info
+get_encompass_info (Grob *me,
+ Grob *col,
+ Grob **common)
+{
+ Grob *stem = unsmob_grob (col->get_property ("stem"));
+ Encompass_info ei;
+ Direction dir = get_grob_direction (me);
+
+ if (!stem)
+ {
+ programming_error ("No stem for note column?");
+ ei.x_ = col->relative_coordinate (common[X_AXIS], X_AXIS);
+ ei.head_ = ei.stem_ = col->extent (common[Y_AXIS],
+ Y_AXIS)[get_grob_direction (me)];
+ return ei;
+ }
+ Direction stem_dir = get_grob_direction (stem);
+
+ if (Grob *head = Note_column::first_head (col))
+ ei.x_ = head->extent (common[X_AXIS], X_AXIS).center ();
+ else
+ ei.x_ = col->extent (common[X_AXIS], X_AXIS).center ();
+
+ Grob *h = Stem::extremal_heads (stem)[Direction (dir)];
+ if (!h)
+ {
+ ei.head_ = ei.stem_ = col->extent (common[Y_AXIS], Y_AXIS)[dir];
+ return ei;
+ }
+
+ ei.head_ = h->extent (common[Y_AXIS], Y_AXIS)[dir];
+
+ if ((stem_dir == dir)
+ && !stem->extent (stem, Y_AXIS).is_empty ())
+ {
+ ei.stem_ = stem->extent (common[Y_AXIS], Y_AXIS)[dir];
+ if (Grob *b = Stem::get_beam (stem))
+ ei.stem_ += stem_dir * 0.5 * Beam::get_thickness (b);
+
+ Interval x = stem->extent (common[X_AXIS], X_AXIS);
+ ei.x_ = x.is_empty ()
+ ? stem->relative_coordinate (common[X_AXIS], X_AXIS)
+ : x.center ();
+ }
+ else
+ ei.stem_ = ei.head_;
+
+ return ei;
+}
+
+
+Direction
+get_default_dir (Grob*me)
+{
+ Link_array<Grob> encompasses
+ = Pointer_group_interface__extract_grobs (me, (Grob*) 0, "note-columns");
+
+ Direction d = DOWN;
+ for (int i= 0; i < encompasses.size (); i ++)
+ {
+ if (Note_column::dir (encompasses[i]) < 0)
+ {
+ d = UP;
+ break;
+ }
+ }
+ return d;
+}
+
+Direction
+get_slur_dir (Grob *slur)
+{
+ Direction d = get_grob_direction (slur);
+ if (d == CENTER)
+ d = get_default_dir (slur);
+ return d;
+}
+
+MAKE_SCHEME_CALLBACK (New_slur, after_line_breaking,1);
+SCM
+New_slur::after_line_breaking (SCM smob)
+{
+ Spanner *me = dynamic_cast<Spanner*> (unsmob_grob (smob));
+ if (!scm_ilength (me->get_property ("note-columns")))
+ {
+ me->suicide ();
+ return SCM_UNSPECIFIED;
+ }
+
+ if (!get_grob_direction (me))
+ set_grob_direction (me, get_default_dir (me));
+
+ if (scm_ilength (me->get_property ("control-points")) < 4)
+ set_end_points (me);
+
+ return SCM_UNSPECIFIED;
+}
+
+Drul_array<Bound_info>
+get_bound_info (Spanner* me, Grob **common)
+{
+ Drul_array<Bound_info> extremes;
+ Direction d = LEFT;
+ Direction dir = get_grob_direction (me);
+
+ do
+ {
+ extremes[d].bound_ = me->get_bound (d);
+ if (Note_column::has_interface (extremes[d].bound_))
+ {
+ extremes[d].note_column_ = extremes[d].bound_;
+ extremes[d].stem_ = Note_column::get_stem (extremes[d].note_column_);
+ extremes[d].stem_dir_ = get_grob_direction (extremes[d].stem_);
+ extremes[d].stem_extent_[X_AXIS]
+ = extremes[d].stem_->extent (common[X_AXIS], X_AXIS);
+ extremes[d].stem_extent_[Y_AXIS]
+ = extremes[d].stem_->extent (common[Y_AXIS], Y_AXIS);
+ extremes[d].slur_head_
+ = Stem::extremal_heads (extremes[d].stem_)[dir];
+ if (!extremes[d].slur_head_
+ && Note_column::has_rests (extremes[d].bound_))
+ {
+ extremes[d].slur_head_ = Note_column::get_rest (extremes[d].bound_);
+ }
+
+ if (extremes[d].slur_head_)
+ extremes[d].slur_head_extent_
+ = extremes[d].slur_head_->extent (common[X_AXIS], X_AXIS);
+
+ extremes[d].staff_ = Staff_symbol_referencer
+ ::get_staff_symbol (extremes[d].stem_);
+ extremes[d].staff_space_ = Staff_symbol_referencer
+ ::staff_space (extremes[d].stem_);
+ }
+ else
+ extremes[d].neighbor_y_ = broken_trend_y (me, common, d);
+ }
+ while (flip (&d) != LEFT);
+ return extremes;
+}
+
+void
+set_end_points (Grob *me)
+{
+
+
+
+ Link_array<Grob> columns
+ = Pointer_group_interface__extract_grobs (me, (Grob *) 0, "note-columns");
+
+ if (columns.is_empty ())
+ {
+ me->suicide ();
+ return;
+ }
+
+ SCM eltlist = me->get_property ("note-columns");
+ SCM extra_list = me->get_property ("encompass-objects");
+ Spanner *sp = dynamic_cast<Spanner*> (me);
+
+ Grob *common[] = {0, 0};
+ for (int i = X_AXIS; i < NO_AXES; i++)
+ {
+ Axis a = (Axis)i;
+ common[a] = common_refpoint_of_list (eltlist, me, a);
+ common[a] = common_refpoint_of_list (extra_list, common[a], a);
+ }
+
+ common[X_AXIS] = common[X_AXIS]->common_refpoint (sp->get_bound (RIGHT),
+ X_AXIS);
+ common[X_AXIS] = common[X_AXIS]->common_refpoint (sp->get_bound (LEFT),
+ X_AXIS);
+
+ Drul_array<Bound_info> extremes = get_bound_info (sp, common);
+ Drul_array<Offset> base_attachment
+ = get_base_attachments (sp, common, extremes);
+ Slur_score_parameters params (me);
+ Drul_array<Real> end_ys
+ = get_y_attachment_range (sp, common, ¶ms, extremes, base_attachment);
+ Array<Slur_score> scores = enumerate_attachments (me, common, ¶ms,
+ extremes, base_attachment,
+ end_ys);
+
+ generate_curves (me, common, ¶ms, extremes, base_attachment, &scores);
+ score_edges (me, common, ¶ms,extremes, base_attachment, &scores);
+ score_slopes (me, common, ¶ms,extremes, base_attachment, &scores);
+ score_encompass (me, common, ¶ms,extremes, base_attachment, &scores);
+ score_extra_encompass (me, common, ¶ms,extremes, base_attachment,
+ &scores);
+
+ Real opt = 1e6;
+ int opt_idx = 0;
+ // why backwards?
+ for (int i = scores.size (); i--;)
+ {
+ if (scores[i].score_ < opt)
+ {
+ opt = scores[i].score_;
+ opt_idx = i;
+ }
+ }
+
+#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")))
+ && ly_c_pair_p (inspect_quants))
+ {
+ Drul_array<Real> ins = ly_scm2interval (inspect_quants);
+ Real mindist = 1e6;
+ for (int i = 0; i < scores.size (); i ++)
+ {
+ Real d =fabs (scores[i].attachment_[LEFT][Y_AXIS] - ins[LEFT])
+ + fabs (scores[i].attachment_[RIGHT][Y_AXIS] - ins[RIGHT]);
+ if (d < mindist)
+ {
+ opt_idx = i;
+ mindist= d;
+ }
+ }
+ if (mindist > 1e5)
+ programming_error ("Could not find quant.");
+ }
+ scores[opt_idx].score_card_ += to_string ("i%d", opt_idx);
+
+ // debug quanting
+ me->set_property ("quant-score",
+ scm_makfrom0str (scores[opt_idx].score_card_.to_str0 ()));
+#endif
+
+ Bezier b = scores[opt_idx].curve_;
+ SCM controls = SCM_EOL;
+ for (int i = 4; i--;)
+ {
+ Offset o = b.control_[i]
+ - Offset (me->relative_coordinate (common[X_AXIS], X_AXIS),
+ me->relative_coordinate (common[Y_AXIS], Y_AXIS));
+ controls = scm_cons (ly_offset2scm (o), controls);
+ }
+ me->set_property ("control-points", controls);
+}
+
+/*
+ TODO: should analyse encompasses to determine sensible region, and
+ should limit slopes available.
+ */
+
+Drul_array<Real>
+get_y_attachment_range (Spanner*me,
+ Grob **common, Slur_score_parameters *score_param,
+ Drul_array<Bound_info> extremes,
+ Drul_array<Offset> base_attachment)
+{
+ Drul_array<Real> end_ys;
+ Direction dir = get_grob_direction (me);
+ Direction d = LEFT;
+ do
+ {
+ if (extremes[d].note_column_)
+ {
+ end_ys[d] = dir
+ * ((dir * (base_attachment[d][Y_AXIS] + score_param->region_size_* dir))
+ >? (dir * (dir + extremes[d].note_column_->extent(common[Y_AXIS],
+ Y_AXIS)[dir]))
+ >? (dir * base_attachment[-d][Y_AXIS]));
+ }
+ else
+ end_ys[d] = extremes[d].neighbor_y_ + score_param->region_size_ * dir;
+ }
+ while (flip (&d) != LEFT);
+
+ return end_ys;
+}
+
+bool
+spanner_less (Spanner *s1, Spanner* s2)
+{
+ Slice b1, b2;
+ Direction d = LEFT;
+ do
+ {
+ b1[d] = s1->get_bound (d)->get_column ()->rank_;
+ b2[d] = s2->get_bound (d)->get_column ()->rank_;
+ } while (flip (&d) != LEFT);
+
+ return b2[LEFT] <= b1[LEFT] && b2[RIGHT] >= b1[RIGHT]
+ && (b2[LEFT] != b1[LEFT] || b2[RIGHT] != b1[RIGHT]);
+}
+
+
+Drul_array<Offset>
+get_base_attachments (Spanner *me,
+ Grob **common, Drul_array<Bound_info> extremes)
+{
+ Link_array<Grob> columns
+ = Pointer_group_interface__extract_grobs (me, (Grob *)0, "note-columns");
+ Drul_array<Offset> base_attachment;
+ Real staff_space = Staff_symbol_referencer::staff_space ((Grob *) me);
+ Direction dir = get_grob_direction (me);
+ Direction d = LEFT;
+ do
+ {
+ Grob *stem = extremes[d].stem_;
+ Grob *head = extremes[d].slur_head_;
+
+ 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];
+ else
+ x = me->get_broken_left_end_align ();
+ }
+ else
+ {
+ bool same_beam =
+ (extremes[d].stem_ && extremes[-d].stem_
+ && Stem::get_beam (extremes[d].stem_) == Stem::get_beam (extremes[-d].stem_));
+
+ /*
+ fixme: X coord should also be set in this case.
+ */
+ if (stem
+ && extremes[d].stem_dir_ == dir
+ && Stem::get_beaming (stem, -d)
+ && (!spanner_less (me, Stem::get_beam (stem))
+ || same_beam))
+ y = extremes[d].stem_extent_[Y_AXIS][dir];
+ else if (head)
+ y = head->extent (common[Y_AXIS], Y_AXIS)[dir];
+ y += dir * 0.5 * staff_space;
+
+ Real pos
+ = (y - extremes[d].staff_->relative_coordinate (common[Y_AXIS],
+ Y_AXIS))
+ * 2.0 / Staff_symbol::staff_space (extremes[d].staff_);
+
+ /* start off staffline. */
+ if (fabs (pos - my_round (pos)) < 0.2
+ && Staff_symbol_referencer::on_staffline (head, (int) rint (pos))
+ && Staff_symbol_referencer::line_count (head) - 1 >= rint (pos)
+ )
+ // TODO: calc from slur thick & line thick, parameter.
+ y += 1.5 * staff_space * dir / 10;
+
+ Grob * fh = Note_column::first_head (extremes[d].note_column_);
+ x =
+ (fh ? fh->extent (common[X_AXIS], X_AXIS)
+ : extremes[d].bound_->extent (common[X_AXIS], X_AXIS))
+ .linear_combination (CENTER);
+ }
+ base_attachment[d] = Offset (x, y);
+
+ } while (flip (&d) != LEFT);
+
+ return base_attachment;
+}
+
+void
+generate_curves (Grob *me, Grob **common,
+ Slur_score_parameters *score_param,
+ Drul_array<Bound_info> extremes,
+ Drul_array<Offset>,
+ Array<Slur_score> *scores)
+{
+ (void) common;
+ (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"));
+ for (int i = 0; i < scores->size(); i++)
+ {
+ 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];
+ (*scores)[i].curve_ = bez;
+ }
+}
+
+Bezier
+avoid_staff_line (Grob *me, Grob **common,
+ Drul_array<Bound_info> extremes,
+ Bezier bez)
+{
+ Offset horiz (1,0);
+ Array<Real> ts = bez.solve_derivative (horiz);
+ Real lt = me->get_paper ()->get_dimension (ly_symbol2scm ("linethickness"));
+ Real thick = robust_scm2double (me->get_property ("thickness"), 1.0) * lt;
+
+ /* TODO: handle case of broken slur. */
+ if (!ts.is_empty ()
+ && (extremes[LEFT].staff_ == extremes[RIGHT].staff_)
+ && extremes[LEFT].staff_ && extremes[RIGHT].staff_)
+ {
+ Real y = bez.curve_point (ts[0])[Y_AXIS];
+
+ Grob *staff = extremes[LEFT].staff_;
+
+ Real staff_space = extremes[LEFT].staff_space_;
+ Real p = 2 * (y - staff->relative_coordinate (common[Y_AXIS], Y_AXIS))
+ / staff_space;
+
+ Real distance = fabs (my_round (p) - p); // in halfspaces
+ if (distance < 4 * thick
+ && (int) fabs (my_round (p))
+ <= 2 * Staff_symbol_referencer::staff_radius (staff) + 0.1
+ && (int (fabs (my_round (p))) % 2
+ != Staff_symbol_referencer::line_count (staff) % 2))
+ {
+ Direction resolution_dir =
+ (distance ? get_grob_direction (me) : Direction (sign (p - my_round(p))));
+
+ // TODO: parameter
+ Real newp = my_round (p) + resolution_dir
+ * 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;
+}
+
+Array<Slur_score>
+enumerate_attachments (Grob *me, Grob *common[],
+ Slur_score_parameters *score_param,
+ Drul_array<Bound_info> extremes,
+ Drul_array<Offset> base_attachment,
+ Drul_array<Real> end_ys)
+{
+ (void) common;
+ /*ugh. */
+ Array<Slur_score> scores;
+
+ Direction dir = get_grob_direction (me);
+ Real staff_space = Staff_symbol_referencer::staff_space ((Grob *) me);
+
+ Drul_array<Offset> os;
+ os[LEFT] = base_attachment[LEFT];
+ Real minimum_length = staff_space
+ * robust_scm2double (me->get_property ("minimum-length"), 2.0);
+
+ for (int i = 0; dir * os[LEFT][Y_AXIS] <= dir * end_ys[LEFT]; i++)
+ {
+ os[RIGHT] = base_attachment[RIGHT];
+ for (int j = 0; dir * os[RIGHT][Y_AXIS] <= dir * end_ys[RIGHT]; j++)
+ {
+ Slur_score s;
+ Direction d = LEFT;
+ Drul_array<bool> attach_to_stem (false, false);
+ do
+ {
+ os[d][X_AXIS] = base_attachment[d][X_AXIS];
+ if (extremes[d].stem_
+ && !Stem::is_invisible (extremes[d].stem_)
+ && extremes[d].stem_dir_ == dir)
+ {
+ if (dir == -d
+ && extremes[d].stem_extent_[Y_AXIS].contains (os[d][Y_AXIS]))
+ {
+ os[d][X_AXIS] = extremes[d].slur_head_extent_[-d]
+ - d * 0.3;
+ attach_to_stem[d] = true;
+ }
+ else if (dir *extremes[d].stem_extent_[Y_AXIS][dir]
+ < dir * os[d][Y_AXIS]
+ && !extremes[d].stem_extent_[X_AXIS].is_empty()
+ )
+
+ os[d][X_AXIS] = extremes[d].stem_extent_[X_AXIS].center();
+ }
+ }
+ while (flip (&d) != LEFT);
+
+ Offset dz;
+ dz = os[RIGHT] - os[LEFT];
+ if (dz[X_AXIS] < minimum_length
+ || fabs (dz[Y_AXIS] / dz[X_AXIS]) > score_param->max_slope_
+ )
+ {
+ do
+ {
+ if (extremes[d].slur_head_)
+ {
+ os[d][X_AXIS] = extremes[d].slur_head_extent_.center ();
+ attach_to_stem[d] = false;
+ }
+ }
+ while (flip (&d) != LEFT);
+ }
+
+ dz = os[RIGHT] - os[LEFT];
+ do
+ {
+ if (extremes[d].slur_head_
+ && !attach_to_stem[d])
+ {
+ /* Horizontally move tilted slurs a little. Move
+ more for bigger tilts.
+
+ TODO: parameter */
+ os[d][X_AXIS]
+ -= dir * extremes[d].slur_head_extent_.length ()
+ * sin (dz.arg ()) / 3;
+ }
+ }
+ while (flip (&d) != LEFT);
+
+ s.attachment_ = os;
+ scores.push (s);
+
+ os[RIGHT][Y_AXIS] += dir * staff_space / 2;
+ }
+
+ os[LEFT][Y_AXIS] += dir * staff_space / 2;
+ }
+ return scores;
+}
+
+void
+score_encompass (Grob *me, Grob *common[],
+ Slur_score_parameters *score_param,
+ Drul_array<Bound_info> extremes,
+ Drul_array<Offset> base_attach,
+ Array<Slur_score> *scores)
+{
+ (void) extremes;
+ (void) base_attach;
+
+ Link_array<Grob> encompasses
+ = Pointer_group_interface__extract_grobs (me, (Grob *)0, "note-columns");
+ Direction dir = get_grob_direction (me);
+
+ Array<Encompass_info> infos;
+
+ for (int i = 0; i < encompasses.size(); i++)
+ infos.push (get_encompass_info (me, encompasses[i], common));
+
+ for (int i = 0; i < scores->size (); i++)
+ {
+ Bezier const &bez (scores->elem (i).curve_);
+ Real demerit = 0.0;
+ for (int j = 0; j < infos.size(); j++)
+ {
+ Real x = infos[j].x_;
+
+ bool l_edge = j==0;
+ 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]))
+ continue;
+
+ Real y = bez.get_other_coordinate (X_AXIS, x);
+ if (!edge)
+ {
+ Real head_dy = (y - infos[j].head_);
+ if (dir * head_dy < 0)
+ demerit += score_param->head_encompass_penalty_;
+ else
+ {
+ Real hd = (head_dy)
+ ? (1 / fabs (head_dy) - 1 / score_param->free_head_distance_)
+ : score_param->head_encompass_penalty_;
+ hd = (hd >? 0)<? score_param->head_encompass_penalty_;
+
+ demerit += hd;
+ }
+ }
+
+ if (dir * (y - infos[j].stem_) < 0)
+ {
+ Real stem_dem =score_param->stem_encompass_penalty_ ;
+ if ((l_edge && dir == UP)
+ || (r_edge && dir == DOWN))
+ stem_dem /= 5;
+
+ demerit += stem_dem;
+ }
+ else if (!edge)
+ {
+ Interval ext;
+ ext.add_point (infos[j].stem_);
+ ext.add_point (infos[j].head_);
+
+ // ?
+ demerit += -score_param->closeness_factor_
+ * (dir
+ * (y - (ext[dir] + dir * score_param->free_head_distance_))
+ <? 0)
+ / infos.size ();
+ }
+ }
+
+#if DEBUG_SLUR_QUANTING
+ (*scores)[i].score_card_ += to_string ("C%.2f", demerit);
+#endif
+
+ (*scores)[i].score_ += demerit;
+ }
+}
+
+void
+score_extra_encompass (Grob *me, Grob *common[],
+ Slur_score_parameters *score_param,
+ Drul_array<Bound_info> extremes,
+ Drul_array<Offset> base_attach,
+ Array<Slur_score> *scores)
+{
+ (void) base_attach;
+ (void) extremes;
+
+ Link_array<Grob> encompasses
+ = Pointer_group_interface__extract_grobs (me, (Grob *)0,
+ "encompass-objects");
+ Direction dir = get_grob_direction (me);
+ 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;
+
+ for (int i = encompasses.size (); i--; )
+ {
+ if (New_slur::has_interface (encompasses[i]))
+ {
+ Grob * small_slur = encompasses[i];
+ Bezier b = New_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));
+ }
+ else
+ {
+ Grob *g = encompasses [i];
+ Interval xe = g->extent (common[X_AXIS], X_AXIS);
+ Interval ye = g->extent (common[Y_AXIS], Y_AXIS);
+
+ Real xp = 0.0;
+
+ if (Accidental_interface::has_interface (g))
+ {
+ /* Begin copy accidental.cc */
+ bool parens = false;
+ if (to_boolean (g->get_property ("cautionary")))
+ {
+ SCM cstyle = g->get_property ("cautionary-style");
+ parens = ly_c_equal_p (cstyle, ly_symbol2scm ("parentheses"));
+ }
+
+ SCM accs = g->get_property ("accidentals");
+ SCM scm_style = g->get_property ("style");
+ if (!ly_c_symbol_p (scm_style)
+ && !parens
+ && scm_ilength (accs) == 1)
+ {
+ /* End copy accidental.cc */
+ switch (ly_scm2int (ly_car (accs)))
+ {
+ case FLAT:
+ case DOUBLE_FLAT:
+ xp = LEFT;
+ break ;
+ case SHARP:
+ xp = 0.5 * dir;
+ break ;
+ case NATURAL:
+ xp = -dir;
+ break;
+ }
+ }
+ }
+
+ xidxs.push (xp);
+ ye.widen (thick * 0.5);
+ yexts.push (ye);
+ xexts.push (xe);
+ }
+ }
+ for (int i = 0; i < scores->size (); i++)
+ {
+ Real demerit = 0.0;
+ for (int j = 0; j < xidxs.size(); j++)
+ {
+ Drul_array<Offset> at = scores->elem (i).attachment_;
+ Interval slur_wid (at[LEFT][X_AXIS], at[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]);
+ Real y = 0.0;
+ if (!slur_wid.contains (x))
+ {
+ Direction contains_dir = CENTER;
+ Direction d = LEFT;
+ do
+ {
+ if (xexts[j].contains (at[d][X_AXIS]))
+ contains_dir = d;
+ }
+ while (flip (&d) != LEFT);
+
+ if (!contains_dir)
+ continue;
+ else
+ y = at[contains_dir][Y_AXIS];
+ }
+ else
+ {
+ 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);
+ demerit +=
+ fabs (0 >? (score_param->extra_encompass_free_distance_ - dist)) /
+ score_param->extra_encompass_free_distance_ * collision_demerit;
+ }
+#if DEBUG_SLUR_QUANTING
+ (*scores)[i].score_card_ += to_string ("X%.2f", demerit);
+#endif
+ (*scores)[i].score_ += demerit;
+ }
+}
+
+void
+score_edges (Grob *me, Grob *common[],
+ Slur_score_parameters * score_param,
+ Drul_array<Bound_info> extremes,
+ Drul_array<Offset> base_attach,
+ Array<Slur_score> *scores)
+{
+ (void) common;
+ Direction dir = get_grob_direction (me);
+
+ for (int i = 0; i < scores->size (); i++)
+ {
+ Direction d = LEFT;
+ do
+ {
+ Real y = scores->elem (i).attachment_[d][Y_AXIS];
+ Real dy = fabs (y - base_attach[d][Y_AXIS]);
+
+ Real factor = score_param->edge_attraction_factor_;
+ Real demerit = factor * dy;
+ if (extremes[d].stem_
+ && extremes[d].stem_dir_ == dir
+ && !Stem::get_beaming (extremes[d].stem_, -d)
+ )
+ demerit /= 5;
+
+ (*scores)[i].score_ += demerit;
+#if DEBUG_SLUR_QUANTING
+ (*scores)[i].score_card_ += to_string ("E%.2f", demerit);
+#endif
+ }
+ while (flip (&d) != LEFT);
+ }
+}
+
+void
+score_slopes (Grob *me, Grob *common[],
+ Slur_score_parameters *score_param,
+ Drul_array<Bound_info> extremes,
+ Drul_array<Offset> base_attach,
+ Array<Slur_score> * scores)
+{
+ (void) me;
+ (void) base_attach;
+
+ Drul_array<Real> ys;
+ Direction d = LEFT;
+ do
+ {
+ if (extremes[d].slur_head_)
+ ys[d] = extremes[d].slur_head_->relative_coordinate (common[Y_AXIS],
+ Y_AXIS);
+ else
+ ys[d] = extremes[d].neighbor_y_;
+ }
+ while (flip (&d) != LEFT);
+
+ bool has_beams
+ = (extremes[LEFT].stem_ && Stem::get_beam (extremes[LEFT].stem_))
+ || (extremes[RIGHT].stem_ && Stem::get_beam (extremes[RIGHT].stem_));
+
+ Real dy = ys[RIGHT] - ys[LEFT];
+ for (int i = 0; i < scores->size (); i++)
+ {
+ Offset slur_dz = (*scores)[i].attachment_[RIGHT]
+ - (*scores)[i].attachment_[LEFT];
+ Real slur_dy = slur_dz[Y_AXIS];
+ Real demerit = 0.0;
+
+ demerit += ((fabs (slur_dy / slur_dz[X_AXIS])
+ - score_param->max_slope_) >? 0)
+ * score_param->max_slope_factor_;
+
+ /* 0.2: account for staffline offset. */
+ Real max_dy = (fabs (dy) + 0.2);
+ if (has_beams)
+ max_dy += 1.0;
+
+ demerit += score_param->steeper_slope_factor_
+ * ((fabs (slur_dy) -max_dy) >? 0);
+
+ demerit += ((fabs (slur_dy/slur_dz[X_AXIS])
+ - score_param->max_slope_) >? 0)
+ * score_param->max_slope_factor_;
+
+ if (sign (dy) == 0
+ && sign (slur_dy) != 0)
+ demerit += score_param->non_horizontal_penalty_;
+
+ if (sign (dy)
+ && sign (slur_dy)
+ && sign (slur_dy) != sign (dy))
+ demerit += has_beams
+ ? score_param->same_slope_penalty_ / 10
+ : score_param->same_slope_penalty_;
+
+#if DEBUG_SLUR_QUANTING
+ (*scores)[i].score_card_ += to_string ("S%.2f", d);
+#endif
+ (*scores)[i].score_ += demerit;
+ }
+
+
+
+}
+
+
+Real
+fit_factor (Offset dz_unit, Offset dz_perp,
+ Bezier curve, Direction d, Array<Offset> const &avoid)
+{
+ Real fit_factor = 0.0;
+ Offset x0 = curve.control_[0];
+ curve.translate (-x0);
+ curve.rotate (-dz_unit.arg ());
+ curve.scale (1, d);
+
+ Interval curve_xext;
+ curve_xext.add_point (curve.control_[0][X_AXIS]);
+ curve_xext.add_point (curve.control_[3][X_AXIS]);
+
+ for (int i = 0; i < avoid.size (); i++)
+ {
+ Offset z = (avoid[i] - x0) ;
+ Offset p (dot_product (z, dz_unit),
+ d* dot_product (z, dz_perp));
+ if (!curve_xext.contains (p[X_AXIS]))
+ continue;
+
+ Real y = curve.get_other_coordinate (X_AXIS, p[X_AXIS]);
+ if (y)
+ {
+ fit_factor = fit_factor >? (p[Y_AXIS] / y);
+ }
+ }
+ return fit_factor;
+}
+
+
+Bezier
+get_bezier (Grob *me,
+ Grob **common,
+ Slur_score_parameters *score_param,
+ Drul_array<Bound_info> extremes,
+ Drul_array<Offset> attachments,
+ 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;
+ for (int i = 0; i < encompasses.size(); i++)
+ {
+ if (extremes[LEFT].note_column_ == encompasses[i]
+ ||extremes[RIGHT].note_column_ == encompasses[i])
+ continue;
+
+ Encompass_info inf (get_encompass_info (me, encompasses[i], common));
+
+ Real y = dir*((dir * inf.head_) >? (dir *inf.stem_));
+
+ avoid.push (Offset (inf.x_, y + dir * score_param->free_head_distance_));
+ }
+
+ Offset dz = attachments[RIGHT]- attachments[LEFT];;
+ Offset dz_unit = dz;
+ dz_unit *= 1 / dz.length();
+ Offset dz_perp = dz_unit * Offset(0,1);
+
+ Real indent, height;
+ get_slur_indent_height (&indent, &height, dz.length (), h_inf, r_0);
+
+ Real excentricity = robust_scm2double (me->get_property ("excentricity"), 0.0);
+ Bezier curve;
+
+ Real x1 = (excentricity + indent);
+ Real x2 = (excentricity - indent);
+ curve.control_[0] = attachments[LEFT];
+ curve.control_[1] = attachments[LEFT] + dz_perp * height * dir + dz_unit * x1;
+ curve.control_[2] = attachments[RIGHT] + dz_perp * height * dir + dz_unit * x2;
+ curve.control_[3] = attachments[RIGHT];
+
+
+ Real ff = fit_factor (dz_unit, dz_perp, curve, dir, avoid);
+ Real l = dz.length ();
+
+ /*
+ This condition,
+
+ l^2 > 4h^2 + 3(i 1/3l)^2 - 1/3 l^2
+
+ is equivalent to:
+
+ |bez'(0)| < | bez'(.5)|
+
+ when (control2-control1) has the same direction as (control3 -
+ control0).
+
+ */
+ Real max_h = sqrt (sqr (l)/3 - .75 * sqr (indent + l / 3));
+ height = height >? ((height * ff) <? max_h);
+
+ curve.control_[0] = attachments[LEFT];
+ curve.control_[1] = attachments[LEFT] + dz_perp * height * dir + dz_unit * x1;
+ curve.control_[2] = attachments[RIGHT] + dz_perp * height * dir + dz_unit * x2;
+ curve.control_[3] = attachments[RIGHT];
+
+ return curve;
+}
--- /dev/null
+/*
+ slur.cc -- implement external interface for Slur
+
+ source file of the GNU LilyPond music typesetter
+
+ (c) 1996--2004 Han-Wen Nienhuys <hanwen@cs.uu.nl>
+ Jan Nieuwenhuizen <janneke@gnu.org>
+*/
+
+
+#include <math.h>
+
+#include "bezier.hh"
+#include "new-slur.hh"
+#include "main.hh"
+#include "font-interface.hh"
+#include "text-item.hh"
+#include "directional-element-interface.hh"
+#include "group-interface.hh"
+#include "lily-guile.hh"
+#include "lookup.hh"
+#include "note-column.hh"
+#include "output-def.hh"
+#include "rod.hh"
+#include "spanner.hh"
+#include "staff-symbol-referencer.hh"
+#include "staff-symbol.hh"
+#include "stem.hh"
+#include "stencil.hh"
+#include "warn.hh"
+#include "beam.hh"
+
+MAKE_SCHEME_CALLBACK (New_slur, height, 2);
+SCM
+New_slur::height (SCM smob, SCM ax)
+{
+ Axis a = (Axis)ly_scm2int (ax);
+ Grob *me = unsmob_grob (smob);
+ assert (a == Y_AXIS);
+
+ SCM mol = me->get_uncached_stencil ();
+ Interval ext;
+ if (Stencil *m = unsmob_stencil (mol))
+ ext = m->extent (a);
+ return ly_interval2scm (ext);
+}
+
+/*
+ Ugh should have dash-length + dash-period
+*/
+MAKE_SCHEME_CALLBACK (New_slur, print,1);
+SCM
+New_slur::print (SCM smob)
+{
+ Grob *me = unsmob_grob (smob);
+ if (!scm_ilength (me->get_property ("note-columns")))
+ {
+ me->suicide ();
+ return SCM_EOL;
+ }
+
+ Real base_thick = robust_scm2double (me->get_property ("thickness"), 1);
+ Real thick = base_thick * Staff_symbol_referencer::line_thickness (me);
+
+ Real ss = Staff_symbol_referencer::staff_space (me);
+ Bezier one = get_curve (me);
+
+ Stencil a;
+
+ /*
+ TODO: replace dashed with generic property.
+ */
+ SCM d = me->get_property ("dashed");
+ if (ly_c_number_p (d))
+ a = Lookup::dashed_slur (one, thick, thick * robust_scm2double (d, 0));
+ else
+ a = Lookup::slur (one, get_grob_direction (me) * base_thick * ss / 10.0,
+ thick);
+
+#if DEBUG_SLUR_QUANTING
+ SCM quant_score = me->get_property ("quant-score");
+
+ if (to_boolean (me->get_paper ()
+ ->lookup_variable (ly_symbol2scm ("debug-slur-quanting")))
+ && ly_c_string_p (quant_score))
+ {
+ String str;
+ SCM properties = Font_interface::text_font_alist_chain (me);
+
+ Stencil tm = *unsmob_stencil (Text_item::interpret_markup
+ (me->get_paper ()->self_scm (), properties,
+ quant_score));
+ a.add_at_edge (Y_AXIS, get_grob_direction (me), tm, 1.0, 0);
+ }
+#endif
+
+ return a.smobbed_copy ();
+}
+
+
+Bezier
+New_slur::get_curve (Grob*me)
+{
+ Bezier b;
+ int i = 0;
+ for (SCM s = me->get_property ("control-points"); s != SCM_EOL;
+ s = ly_cdr (s))
+ b.control_[i++] = ly_scm2offset (ly_car (s));
+
+ return b;
+}
+
+void
+New_slur::add_column (Grob*me, Grob*n)
+{
+ Pointer_group_interface::add_grob (me, ly_symbol2scm ("note-columns"), n);
+ add_bound_item (dynamic_cast<Spanner*> (me), dynamic_cast<Item*> (n));
+}
+
+
+void
+New_slur::add_extra_encompass (Grob*me, Grob*n)
+{
+ Pointer_group_interface::add_grob (me, ly_symbol2scm ("encompass-objects"), n);
+}
+
+
+
+MAKE_SCHEME_CALLBACK (New_slur, outside_slur_callback, 2);
+SCM
+New_slur::outside_slur_callback (SCM grob, SCM axis)
+{
+ Grob *script = unsmob_grob (grob);
+ Axis a = Axis (ly_scm2int (axis));
+ assert (a == Y_AXIS);
+
+ Grob *slur = unsmob_grob (script->get_property ("slur"));
+
+ if (!slur)
+ return scm_from_int (0);
+
+ Grob *cx = script->common_refpoint (slur, X_AXIS);
+ Grob *cy = script->common_refpoint (slur, Y_AXIS);
+
+ Bezier curve = New_slur::get_curve (slur);
+
+ curve.translate (Offset (slur->relative_coordinate (cx, X_AXIS),
+ slur->relative_coordinate (cy, Y_AXIS)));
+
+ Interval yext = robust_relative_extent (script, cy, Y_AXIS);
+ Interval xext = robust_relative_extent (script, cx, X_AXIS);
+
+
+ Real slur_padding = 0.2; // todo: slur property, script property?
+ yext.widen (slur_padding);
+
+ Interval bezext (curve.control_[0][X_AXIS],
+ curve.control_[3][X_AXIS]);
+
+ Real x = xext.center ();
+ if (!bezext.contains (xext[RIGHT]))
+ x = xext[LEFT];
+ else if (!bezext.contains (xext[LEFT]))
+ x = xext[RIGHT];
+
+
+ if (!bezext.contains (x))
+ return scm_make_real (0);
+
+ Real y = curve.get_other_coordinate (X_AXIS, x);
+ if (yext.contains (y))
+ {
+ Direction dir = get_grob_direction (script);
+ return scm_make_real (y - yext[-dir] + dir * slur_padding);
+ }
+ return scm_make_real (0.0);
+}
+
+
+ADD_INTERFACE (New_slur, "new-slur-interface",
+ "A slur",
+ "excentricity encompass-objects control-points dashed slur-details direction height-limit note-columns ratio thickness");
+