2 slur.cc -- implement Slur
4 source file of the GNU LilyPond music typesetter
6 (c) 1996--2000 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7 Jan Nieuwenhuizen <janneke@gnu.org>
12 * fix broken interstaff slurs
13 * begin and end should be treated as a/acknowledge Scripts.
14 * broken slur should have uniform trend
15 * smart changing of endings
16 * smart changing of (Y-?)offsets to avoid ugly beziers
20 #include "directional-element-interface.hh"
21 #include "group-interface.hh"
24 #include "paper-def.hh"
25 #include "note-column.hh"
27 #include "paper-column.hh"
28 #include "molecule.hh"
30 #include "slur-bezier-bow.hh"
32 #include "group-interface.hh"
33 #include "staff-symbol-referencer.hh"
38 Slur::set_interface (Score_element
*me
)
40 me
->set_elt_property ("attachment", gh_cons (SCM_BOOL_F
, SCM_BOOL_F
));
41 me
->set_interface (ly_symbol2scm ("slur-interface"));
45 Slur::add_column (Score_element
*me
, Score_element
*n
)
47 if (!gh_pair_p (n
->get_elt_property ("note-heads")))
48 warning (_ ("Putting slur over rest. Ignoring."));
51 Pointer_group_interface (me
, "note-columns").add_element (n
);
52 me
->add_dependency (n
);
55 add_bound_item (dynamic_cast<Spanner
*> (me
), dynamic_cast<Item
*>(n
));
59 Slur::de_uglyfy (Score_element
*me
, Slur_bezier_bow
* bb
, Real default_height
)
61 Real length
= bb
->curve_
.control_
[3][X_AXIS
] ;
62 Real ff
= bb
->fit_factor ();
63 for (int i
= 1; i
< 3; i
++)
65 Real ind
= abs (bb
->curve_
.control_
[(i
-1)*3][X_AXIS
]
66 - bb
->curve_
.control_
[i
][X_AXIS
]) / length
;
67 Real h
= bb
->curve_
.control_
[i
][Y_AXIS
] * ff
/ length
;
69 Real f
= default_height
/ length
;
70 Real c1
= me
->paper_l ()->get_var ("bezier_control1");
71 Real c2
= me
->paper_l ()->get_var ("bezier_control2");
72 Real c3
= me
->paper_l ()->get_var ("bezier_control3");
77 else if (h
> c2
+ c3
* ind
)
82 bb
->curve_
.control_
[i
][Y_AXIS
] = h
* length
;
85 bb
->curve_
.assert_sanity ();
89 Slur::get_default_dir (Score_element
*me
)
91 Link_array
<Score_element
> encompass_arr
=
92 Pointer_group_interface__extract_elements (me
, (Score_element
*)0, "note-columns");
95 for (int i
=0; i
< encompass_arr
.size (); i
++)
97 if (Note_column::dir (encompass_arr
[i
]) < 0)
111 Slur::encompass_offset (Score_element
*me
,
113 Score_element
**common
)
116 Score_element
* stem_l
= unsmob_element (col
->get_elt_property ("stem"));
118 Direction dir
= Directional_element_interface (me
).get ();
122 warning (_ ("Slur over rest?"));
123 o
[X_AXIS
] = col
->relative_coordinate (common
[X_AXIS
], X_AXIS
);
124 o
[Y_AXIS
] = col
->relative_coordinate (common
[Y_AXIS
], Y_AXIS
);
127 Direction stem_dir
= Directional_element_interface (stem_l
).get ();
128 o
[X_AXIS
] = stem_l
->relative_coordinate (0, X_AXIS
);
131 Simply set x to middle of notehead
134 o
[X_AXIS
] -= 0.5 * stem_dir
* col
->extent (X_AXIS
).length ();
136 if ((stem_dir
== dir
)
137 && !stem_l
->extent (Y_AXIS
).empty_b ())
139 o
[Y_AXIS
] = stem_l
->relative_coordinate (common
[Y_AXIS
], Y_AXIS
); // iuhg
143 o
[Y_AXIS
] = col
->relative_coordinate (common
[Y_AXIS
], Y_AXIS
); // ugh
147 leave a gap: slur mustn't touch head/stem
149 o
[Y_AXIS
] += dir
* me
->paper_l ()->get_var ("slur_y_free");
153 MAKE_SCHEME_CALLBACK(Slur
,after_line_breaking
);
156 Slur::after_line_breaking (SCM smob
)
158 Score_element
*me
= unsmob_element (smob
);
159 set_extremities (me
);
160 set_control_points (me
);
161 return SCM_UNSPECIFIED
;
165 Slur::set_extremities (Score_element
*me
)
167 if (!Directional_element_interface (me
).get ())
168 Directional_element_interface (me
).set (get_default_dir (me
));
170 Direction dir
= LEFT
;
173 if (!gh_symbol_p (index_cell (me
->get_elt_property ("attachment"), dir
)))
176 // for (SCM s = get_elt_property ("slur-extremity-rules"); s != SCM_EOL; s = gh_cdr (s))
177 for (SCM s
= scm_eval (ly_symbol2scm ("slur-extremity-rules"));
178 s
!= SCM_EOL
; s
= gh_cdr (s
))
180 SCM r
= gh_call2 (gh_caar (s
), me
->self_scm (),
181 gh_int2scm ((int)dir
));
184 index_set_cell (me
->get_elt_property ("attachment"), dir
,
191 while (flip (&dir
) != LEFT
);
195 Slur::get_attachment (Score_element
*me
,Direction dir
,
196 Score_element
**common
)
198 Spanner
*sp
= dynamic_cast<Spanner
*>(me
);
199 SCM s
= me
->get_elt_property ("attachment");
200 SCM a
= dir
== LEFT
? gh_car (s
) : gh_cdr (s
);
201 String str
= ly_symbol2string (a
);
202 Real ss
= Staff_symbol_referencer::staff_space ((Score_element
*)me
);
207 if (Note_column::has_interface (sp
->get_bound (dir
)))
209 Score_element
* n
=sp
->get_bound (dir
);
210 if (Score_element
*st
= Note_column::stem_l (n
))
215 o
= Offset (0, Stem::chord_start_f (st
));
217 Default position is centered in X, on outer side of head Y
219 o
+= Offset (0.5 * n
->extent (X_AXIS
).length (),
220 0.5 * ss
* Directional_element_interface (me
).get ());
222 else if (str
== "alongside-stem")
224 o
= Offset (0, Stem::chord_start_f (st
));
226 Default position is on stem X, on outer side of head Y
228 o
+= Offset (n
->extent (X_AXIS
).length ()
229 * (1 + Stem::get_direction (st
)),
230 0.5 * ss
* Directional_element_interface (me
).get ());
232 else if (str
== "stem")
234 o
= Offset (0, Stem::stem_end_position (st
) * hs
);
236 Default position is on stem X, at stem end Y
239 (n
->extent (X_AXIS
).length ()
240 - st
->extent (X_AXIS
).length ())
241 * (1 + Stem::get_direction (st
)),
244 else if (str
== "loose-end")
246 SCM other_a
= dir
== LEFT
? gh_cdr (s
) : gh_car (s
);
247 if (ly_symbol2string (other_a
) != "loose-end")
249 o
= Offset (0, get_attachment (me
, -dir
, common
)[Y_AXIS
]);
256 gh_int2scm (Stem::get_direction (st
) * dir
),
257 gh_int2scm (Directional_element_interface (me
).get () * dir
),
259 scm_eval (ly_symbol2scm ("slur-extremity-offset-alist")));
263 o
+= ly_scm2offset (gh_cdr (l
)) * ss
* dir
;
270 What if get_bound () is not a note-column?
272 if (str
!= "loose-end"
273 && sp
->get_bound (dir
)->common_refpoint (common
[Y_AXIS
], Y_AXIS
) == common
[Y_AXIS
])
275 o
[Y_AXIS
] += sp
->get_bound (dir
)->relative_coordinate (common
[Y_AXIS
], Y_AXIS
)
276 - me
->relative_coordinate (common
[Y_AXIS
], Y_AXIS
);
282 Slur::get_encompass_offset_arr (Score_element
*me
)
284 Spanner
*sp
= dynamic_cast<Spanner
*>(me
);
285 SCM eltlist
= me
->get_elt_property ("note-columns");
286 Score_element
*common
[] = {me
->common_refpoint (eltlist
,X_AXIS
),
287 me
->common_refpoint (eltlist
,Y_AXIS
)};
290 common
[X_AXIS
] = common
[X_AXIS
]->common_refpoint (sp
->get_bound (RIGHT
), X_AXIS
);
291 common
[X_AXIS
] = common
[X_AXIS
]->common_refpoint (sp
->get_bound (LEFT
), X_AXIS
);
293 Link_array
<Score_element
> encompass_arr
;
294 while (gh_pair_p (eltlist
))
296 encompass_arr
.push (unsmob_element (gh_car (eltlist
)));
297 eltlist
=gh_cdr (eltlist
);
299 encompass_arr
.reverse ();
302 Array
<Offset
> offset_arr
;
304 Offset
origin (me
->relative_coordinate (common
[X_AXIS
], X_AXIS
),
305 me
->relative_coordinate (common
[Y_AXIS
], Y_AXIS
));
308 int last
= encompass_arr
.size () - 2;
310 offset_arr
.push (get_attachment (me
, LEFT
, common
));
316 if (encompass_arr
[0] != sp
->get_bound (LEFT
))
321 offset_arr
[0][Y_AXIS
] -=
322 encompass_arr
[0]->relative_coordinate (common
[Y_AXIS
], Y_AXIS
)
323 - me
->relative_coordinate (common
[Y_AXIS
], Y_AXIS
);
329 if (encompass_arr
.top () != sp
->get_bound (RIGHT
))
334 for (int i
= first
; i
<= last
; i
++)
336 Offset
o (encompass_offset (me
, encompass_arr
[i
], common
));
337 offset_arr
.push (o
- origin
);
340 offset_arr
.push (Offset (sp
->spanner_length (), 0) + get_attachment (me
, RIGHT
,common
));
342 if (encompass_arr
[0] != sp
->get_bound (LEFT
))
344 offset_arr
.top ()[Y_AXIS
] -= encompass_arr
.top ()->relative_coordinate (common
[Y_AXIS
], Y_AXIS
)
345 - me
->relative_coordinate (common
[Y_AXIS
], Y_AXIS
);
352 MAKE_SCHEME_CALLBACK(Slur
,set_spacing_rods
);
354 Slur::set_spacing_rods (SCM smob
)
356 Score_element
*me
= unsmob_element (smob
);
359 Spanner
*sp
= dynamic_cast<Spanner
*>(me
);
360 r
.item_l_drul_
[LEFT
] = sp
->get_bound (LEFT
);
361 r
.item_l_drul_
[RIGHT
] = sp
->get_bound (RIGHT
);
363 gh_scm2double (me
->get_elt_property ("minimum-length"))
364 * me
->paper_l ()->get_var ("staffspace");
367 return SCM_UNSPECIFIED
;
372 Ugh should have dash-length + dash-period
374 MAKE_SCHEME_CALLBACK(Slur
,brew_molecule
);
376 Slur::brew_molecule (SCM smob
)
378 Score_element
* me
= unsmob_element (smob
);
379 Real thick
= me
->paper_l ()->get_var ("stafflinethickness") *
380 gh_scm2double (me
->get_elt_property ("thickness"));
381 Bezier one
= get_curve (me
);
384 SCM d
= me
->get_elt_property ("dashed");
386 a
= me
->lookup_l ()->dashed_slur (one
, thick
, thick
* gh_scm2double (d
));
388 a
= me
->lookup_l ()->slur (one
, Directional_element_interface (me
).get () * thick
, thick
);
390 return a
.create_scheme();
394 Slur::set_control_points (Score_element
*me
)
396 Real staff_space
= Staff_symbol_referencer::staff_space ((Score_element
*)me
);
397 Real h_inf
= me
->paper_l ()->get_var ("slur_height_limit_factor") *
399 Real r_0
= me
->paper_l ()->get_var ("slur_ratio");
401 Slur_bezier_bow
bb (get_encompass_offset_arr (me
),
402 Directional_element_interface (me
).get (),
405 if (bb
.fit_factor () > 1.0)
407 Real length
= bb
.curve_
.control_
[3][X_AXIS
];
408 Real default_height
= slur_height (length
, h_inf
, r_0
);
409 bb
.minimise_enclosed_area (me
->paper_l());
411 Real bff
= me
->paper_l ()->get_var ("slur_force_blowfit");
412 bb
.curve_
.control_
[1][Y_AXIS
] *= bff
;
413 bb
.curve_
.control_
[2][Y_AXIS
] *= bff
;
416 Real sb
= me
->paper_l ()->get_var ("slur_beautiful");
417 Real beautiful
= length
* default_height
* sb
;
418 Real area
= bb
.enclosed_area_f ();
421 Slurs that fit beautifully are not ugly
423 if (area
> beautiful
)
424 de_uglyfy (me
, &bb
, default_height
);
427 Bezier b
= bb
.get_bezier ();
430 SCM controls
= SCM_EOL
;
432 controls
= gh_cons ( ly_offset2scm (b
.control_
[i
]), controls
);
434 me
->set_elt_property ("control-points", controls
);
439 Slur::get_curve (Score_element
*me
)
444 if (!Directional_element_interface (me
).get ()
445 || ! gh_symbol_p (index_cell (me
->get_elt_property ("attachment"), LEFT
)))
446 set_extremities (me
);
448 if (!gh_pair_p (me
->get_elt_property ("control-points")))
449 set_control_points (me
);
452 for (SCM s
= me
->get_elt_property ("control-points"); s
!= SCM_EOL
; s
= gh_cdr (s
))
454 b
.control_
[i
] = ly_scm2offset (gh_car (s
));
458 Array
<Offset
> enc (get_encompass_offset_arr (me
));
459 Direction dir
= Directional_element_interface (me
).get ();
461 Real x1
= enc
[0][X_AXIS
];
462 Real x2
= enc
.top ()[X_AXIS
];
465 for (int i
=1; i
< enc
.size ()-1; i
++)
467 Real x
= enc
[i
][X_AXIS
];
470 Real y
= b
.get_other_coordinate (X_AXIS
, x
);
471 off
= off
>? dir
* (enc
[i
][Y_AXIS
] - y
);
474 b
.translate (Offset (0, dir
* off
));
480 Slur::has_interface (Score_element
*me
)
482 return me
->has_interface (ly_symbol2scm ("slur-interface"));