2 slur.cc -- implement Slur
4 source file of the GNU LilyPond music typesetter
6 (c) 1996--2001 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7 Jan Nieuwenhuizen <janneke@gnu.org>
12 * should avoid stafflines with horizontal part.
13 * begin and end should be treated as a/acknowledge Scripts.
14 * smart changing of endings
15 * smart changing of (Y-?)offsets to avoid ugly beziers
19 #include "directional-element-interface.hh"
20 #include "group-interface.hh"
23 #include "paper-def.hh"
24 #include "note-column.hh"
26 #include "paper-column.hh"
27 #include "molecule.hh"
29 #include "slur-bezier-bow.hh"
31 #include "group-interface.hh"
32 #include "staff-symbol-referencer.hh"
37 Slur::set_interface (Grob
*me
)
39 /* Copy to mutable list. */
40 me
->set_grob_property ("attachment",
41 ly_deep_copy (me
->get_grob_property ("attachment")));
45 Slur::add_column (Grob
*me
, Grob
*n
)
47 if (!gh_pair_p (n
->get_grob_property ("note-heads")))
48 warning (_ ("Putting slur over rest. Ignoring."));
51 Pointer_group_interface::add_element (me
, "note-columns",n
);
52 me
->add_dependency (n
);
55 add_bound_item (dynamic_cast<Spanner
*> (me
), dynamic_cast<Item
*> (n
));
59 Slur::de_uglyfy (Grob
*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 SCM up
= me
->get_grob_property ("de-uglify-parameters");
72 Real c1
= gh_scm2double (gh_car (up
));
73 Real c2
= gh_scm2double (gh_cadr (up
));
74 Real c3
= gh_scm2double (gh_caddr (up
));
80 else if (h
> c2
+ c3
* ind
)
85 bb
->curve_
.control_
[i
][Y_AXIS
] = h
* length
;
88 bb
->curve_
.assert_sanity ();
92 Slur::get_default_dir (Grob
*me
)
94 Link_array
<Grob
> encompass_arr
=
95 Pointer_group_interface__extract_elements (me
, (Grob
*)0, "note-columns");
98 for (int i
=0; i
< encompass_arr
.size (); i
++)
100 if (Note_column::dir (encompass_arr
[i
]) < 0)
110 MAKE_SCHEME_CALLBACK (Slur
, after_line_breaking
,1);
112 Slur::after_line_breaking (SCM smob
)
114 Grob
*me
= unsmob_grob (smob
);
115 if (!scm_ilength (me
->get_grob_property ("note-columns")))
118 return SCM_UNSPECIFIED
;
120 set_extremities (me
);
121 set_control_points (me
);
122 return SCM_UNSPECIFIED
;
127 Slur::check_slope (Grob
*me
)
130 Avoid too steep slurs.
132 SCM s
= me
->get_grob_property ("slope-limit");
135 Array
<Offset
> encompass
= get_encompass_offset_arr (me
);
136 Drul_array
<Offset
> attachment
;
137 attachment
[LEFT
] = encompass
[0];
138 attachment
[RIGHT
] = encompass
.top ();
140 Real dx
= attachment
[RIGHT
][X_AXIS
] - attachment
[LEFT
][X_AXIS
];
141 Real dy
= attachment
[RIGHT
][Y_AXIS
] - attachment
[LEFT
][Y_AXIS
];
145 Real slope
= slope
= abs (dy
/ dx
);
147 Real limit
= gh_scm2double (s
);
151 Real staff_space
= Staff_symbol_referencer::staff_space ((Grob
*)me
);
152 Direction dir
= (Direction
)gh_scm2int (me
->get_grob_property ("direction"));
153 Direction d
= (Direction
) (- dir
* (sign (dy
)));
154 SCM a
= me
->get_grob_property ("attachment-offset");
155 Drul_array
<Offset
> o
;
156 o
[LEFT
] = ly_scm2offset (index_cell (a
, LEFT
));
157 o
[RIGHT
] = ly_scm2offset (index_cell (a
, RIGHT
));
158 o
[d
][Y_AXIS
] -= (limit
- slope
) * dx
* dir
/ staff_space
;
160 o
[d
][Y_AXIS
] *= Directional_element_interface::get (me
);
162 me
->set_grob_property ("attachment-offset",
163 gh_cons (ly_offset2scm (o
[LEFT
]),
164 ly_offset2scm (o
[RIGHT
])));
170 Slur::set_extremities (Grob
*me
)
172 if (!Directional_element_interface::get (me
))
173 Directional_element_interface::set (me
, get_default_dir (me
));
175 Direction dir
= LEFT
;
178 if (!gh_symbol_p (index_cell (me
->get_grob_property ("attachment"), dir
)))
180 for (SCM s
= me
->get_grob_property ("extremity-rules");
181 s
!= SCM_EOL
; s
= gh_cdr (s
))
183 SCM r
= gh_call2 (gh_caar (s
), me
->self_scm (),
184 gh_int2scm ((int)dir
));
187 index_set_cell (me
->get_grob_property ("attachment"), dir
,
194 while (flip (&dir
) != LEFT
);
201 Slur::get_first_notecolumn_y (Grob
*me
, Direction dir
)
203 Grob
*col
= dir
== LEFT
204 ? unsmob_grob (gh_car (scm_reverse (me
->get_grob_property
207 (gh_car (me
->get_grob_property ("note-columns")));
212 me
->common_refpoint (col
, Y_AXIS
)
215 if (col
== ((Spanner
*)me
)->get_bound (dir
))
217 y
= get_attachment (me
, dir
, common
)[Y_AXIS
];
221 y
= encompass_offset (me
, col
, common
)[Y_AXIS
]
222 - me
->relative_coordinate (common
[Y_AXIS
], Y_AXIS
);
228 Slur::broken_trend_offset (Grob
*me
, Direction dir
)
231 A broken slur should maintain the same vertical trend
232 the unbroken slur would have had.
235 if (Spanner
*mother
= dynamic_cast<Spanner
*> (me
->original_l_
))
237 for (int i
= dir
== LEFT
? 0 : mother
->broken_into_l_arr_
.size () - 1;
238 dir
== LEFT
? i
< mother
->broken_into_l_arr_
.size () : i
> 0;
239 dir
== LEFT
? i
++ : i
--)
241 if (mother
->broken_into_l_arr_
[i
- dir
] == me
)
243 Grob
*neighbour
= mother
->broken_into_l_arr_
[i
];
245 neighbour
->set_grob_property ("direction",
246 me
->get_grob_property ("direction"));
247 Real neighbour_y
= get_first_notecolumn_y (neighbour
, dir
);
248 Real y
= get_first_notecolumn_y (me
, -dir
);
249 int neighbour_cols
= scm_ilength (neighbour
->get_grob_property ("note-columns"));
250 int cols
= scm_ilength (me
->get_grob_property ("note-columns"));
251 o
= Offset (0, (y
*neighbour_cols
+ neighbour_y
*cols
) /
252 (cols
+ neighbour_cols
));
261 Slur::get_attachment (Grob
*me
, Direction dir
,
264 SCM s
= me
->get_grob_property ("attachment");
265 if (!gh_symbol_p (index_cell (s
, dir
)))
267 set_extremities (me
);
268 s
= me
->get_grob_property ("attachment");
270 SCM a
= dir
== LEFT
? gh_car (s
) : gh_cdr (s
);
271 Spanner
*sp
= dynamic_cast<Spanner
*> (me
);
272 String str
= ly_symbol2string (a
);
273 Real staff_space
= Staff_symbol_referencer::staff_space ((Grob
*)me
);
274 Real hs
= staff_space
/ 2.0;
278 if (Note_column::has_interface (sp
->get_bound (dir
)))
280 Grob
* n
=sp
->get_bound (dir
);
281 if ((stem
= Note_column::stem_l (n
)))
284 if (Grob
*head
= Note_column::first_head (n
))
285 x_extent
= head
->extent (head
, X_AXIS
).length ();
287 x_extent
= n
->extent (n
, X_AXIS
).length ();
291 o
= Offset (0, Stem::head_positions (stem
)
292 [Directional_element_interface::get (me
)] * hs
);
294 Default position is centered in X, on outer side of head Y
296 o
+= Offset (0.5 * x_extent
,
298 * Directional_element_interface::get (me
));
300 else if (str
== "alongside-stem")
302 o
= Offset (0, Stem::chord_start_f (stem
));
304 Default position is on stem X, on outer side of head Y
306 o
+= Offset (x_extent
* (1 + Stem::get_direction (stem
)),
308 * Directional_element_interface::get (me
));
310 else if (str
== "stem")
312 o
= Offset (0, Stem::stem_end_position (stem
) * hs
);
314 Default position is on stem X, at stem end Y
317 x_extent
* (1 + Stem::get_direction (stem
)),
323 If we're not a note_column, we can't be anything but a loose-end.
324 But if user has set (attachment . (stem . stem)), our string is
327 Hmm, maybe after-line-breaking should set this to loose-end? */
328 else // if (str == "loose-end")
330 SCM other_a
= dir
== LEFT
? gh_cdr (s
) : gh_car (s
);
331 if (ly_symbol2string (other_a
) != "loose-end")
332 o
= broken_trend_offset (me
, dir
);
335 SCM alist
= me
->get_grob_property ("extremity-offset-alist");
336 int stemdir
= stem
? Stem::get_direction (stem
) : 1;
337 int slurdir
= gh_scm2int (me
->get_grob_property ("direction"));
340 gh_int2scm (stemdir
* dir
),
341 gh_int2scm (slurdir
* dir
),
342 SCM_UNDEFINED
), alist
);
346 Offset off
= ly_scm2offset (gh_cdr (l
)) * staff_space
;
348 off
[Y_AXIS
] *= Directional_element_interface::get (me
);
353 What if get_bound () is not a note-column?
355 if (str
!= "loose-end"
356 && sp
->get_bound (dir
)->common_refpoint (common
[Y_AXIS
], Y_AXIS
) == common
[Y_AXIS
])
358 o
[Y_AXIS
] += sp
->get_bound (dir
)->relative_coordinate (common
[Y_AXIS
], Y_AXIS
)
359 - me
->relative_coordinate (common
[Y_AXIS
], Y_AXIS
);
362 Offset off
= ly_scm2offset (index_cell (me
->get_grob_property
363 ("attachment-offset"),
366 off
[Y_AXIS
] *= Directional_element_interface::get (me
);
372 Slur::encompass_offset (Grob
*me
,
377 Grob
* stem_l
= unsmob_grob (col
->get_grob_property ("stem"));
379 Direction dir
= Directional_element_interface::get (me
);
383 warning (_ ("Slur over rest?"));
384 o
[X_AXIS
] = col
->relative_coordinate (common
[X_AXIS
], X_AXIS
);
385 o
[Y_AXIS
] = col
->relative_coordinate (common
[Y_AXIS
], Y_AXIS
);
388 Direction stem_dir
= Directional_element_interface::get (stem_l
);
389 o
[X_AXIS
] = stem_l
->relative_coordinate (0, X_AXIS
);
392 Simply set x to middle of notehead
395 if (Grob
*head
= Note_column::first_head (col
))
396 x_extent
= head
->extent (head
, X_AXIS
).length ();
398 x_extent
= col
->extent (col
, X_AXIS
).length ();
399 o
[X_AXIS
] -= 0.5 * stem_dir
* x_extent
;
401 if ((stem_dir
== dir
)
402 && !stem_l
->extent (stem_l
, Y_AXIS
).empty_b ())
404 o
[Y_AXIS
] = stem_l
->extent (common
[Y_AXIS
], Y_AXIS
)[dir
];
408 o
[Y_AXIS
] = col
->extent (common
[Y_AXIS
], Y_AXIS
)[dir
];
412 leave a gap: slur mustn't touch head/stem
414 o
[Y_AXIS
] += dir
* gh_scm2double (me
->get_grob_property ("y-free")) *
420 Slur::get_encompass_offset_arr (Grob
*me
)
422 Spanner
*sp
= dynamic_cast<Spanner
*> (me
);
423 SCM eltlist
= me
->get_grob_property ("note-columns");
424 Grob
*common
[] = {me
->common_refpoint (eltlist
, X_AXIS
),
425 me
->common_refpoint (eltlist
, Y_AXIS
)};
428 common
[X_AXIS
] = common
[X_AXIS
]->common_refpoint (sp
->get_bound (RIGHT
), X_AXIS
);
429 common
[X_AXIS
] = common
[X_AXIS
]->common_refpoint (sp
->get_bound (LEFT
), X_AXIS
);
431 Link_array
<Grob
> encompass_arr
;
432 while (gh_pair_p (eltlist
))
434 encompass_arr
.push (unsmob_grob (gh_car (eltlist
)));
435 eltlist
=gh_cdr (eltlist
);
437 encompass_arr
.reverse ();
440 Array
<Offset
> offset_arr
;
442 Offset
origin (me
->relative_coordinate (common
[X_AXIS
], X_AXIS
),
443 me
->relative_coordinate (common
[Y_AXIS
], Y_AXIS
));
446 int last
= encompass_arr
.size () - 2;
448 offset_arr
.push (get_attachment (me
, LEFT
, common
));
454 if (encompass_arr
[0] != sp
->get_bound (LEFT
))
459 offset_arr
[0][Y_AXIS
] -=
460 encompass_arr
[0]->relative_coordinate (common
[Y_AXIS
], Y_AXIS
)
461 - me
->relative_coordinate (common
[Y_AXIS
], Y_AXIS
);
467 if (encompass_arr
.top () != sp
->get_bound (RIGHT
))
472 for (int i
= first
; i
<= last
; i
++)
474 Offset
o (encompass_offset (me
, encompass_arr
[i
], common
));
475 offset_arr
.push (o
- origin
);
478 offset_arr
.push (Offset (sp
->spanner_length (), 0) + get_attachment (me
, RIGHT
,common
));
480 if (encompass_arr
[0] != sp
->get_bound (LEFT
))
482 offset_arr
.top ()[Y_AXIS
] -= encompass_arr
.top ()->relative_coordinate (common
[Y_AXIS
], Y_AXIS
)
483 - me
->relative_coordinate (common
[Y_AXIS
], Y_AXIS
);
495 MAKE_SCHEME_CALLBACK (Slur
, height
, 2);
497 Slur::height (SCM smob
, SCM ax
)
499 Axis a
= (Axis
)gh_scm2int (ax
);
500 Grob
* me
= unsmob_grob (smob
);
501 assert (a
== Y_AXIS
);
503 SCM mol
= me
->get_uncached_molecule ();
504 return ly_interval2scm (unsmob_molecule (mol
)->extent (a
));
508 Ugh should have dash-length + dash-period
510 MAKE_SCHEME_CALLBACK (Slur
, brew_molecule
,1);
512 Slur::brew_molecule (SCM smob
)
514 Grob
* me
= unsmob_grob (smob
);
515 if (!scm_ilength (me
->get_grob_property ("note-columns")))
521 Real thick
= me
->paper_l ()->get_var ("stafflinethickness") *
522 gh_scm2double (me
->get_grob_property ("thickness"));
523 Bezier one
= get_curve (me
);
525 // get_curve may suicide
526 if (!scm_ilength (me
->get_grob_property ("note-columns")))
530 SCM d
= me
->get_grob_property ("dashed");
532 a
= Lookup::dashed_slur (one
, thick
, thick
* gh_scm2double (d
));
534 a
= Lookup::slur (one
, Directional_element_interface::get (me
) * thick
, thick
);
536 return a
.smobbed_copy ();
540 Slur::set_control_points (Grob
*me
)
542 Real staff_space
= Staff_symbol_referencer::staff_space ((Grob
*)me
);
544 SCM details
= me
->get_grob_property ("details");
545 SCM h_inf_scm
= scm_assq (ly_symbol2scm ("height-limit"), details
);
546 SCM r_0_scm
= scm_assq (ly_symbol2scm ("ratio"), details
);
548 Real r_0
= gh_scm2double (gh_cdr (r_0_scm
));
549 Real h_inf
= staff_space
* gh_scm2double (gh_cdr (h_inf_scm
));
551 Slur_bezier_bow
bb (get_encompass_offset_arr (me
),
552 Directional_element_interface::get (me
),
555 if (bb
.fit_factor () > 1.0)
557 Real length
= bb
.curve_
.control_
[3][X_AXIS
];
558 Real default_height
= slur_height (length
, h_inf
, r_0
);
560 SCM ssb
= me
->get_grob_property ("beautiful");
562 if (gh_number_p (ssb
))
563 sb
= gh_scm2double (ssb
);
565 bb
.minimise_enclosed_area (sb
, details
);
566 SCM sbf
= scm_assq (ly_symbol2scm ("force-blowfit"), details
);
568 if (gh_pair_p (sbf
) && gh_number_p (gh_cdr (sbf
)))
569 bff
= gh_scm2double (gh_cdr (sbf
));
571 bb
.curve_
.control_
[1][Y_AXIS
] *= bff
;
572 bb
.curve_
.control_
[2][Y_AXIS
] *= bff
;
576 Real beautiful
= length
* default_height
* sb
;
577 Real area
= bb
.enclosed_area_f ();
580 Slurs that fit beautifully are not ugly
582 if (area
> beautiful
)
583 de_uglyfy (me
, &bb
, default_height
);
586 Bezier b
= bb
.get_bezier ();
589 SCM controls
= SCM_EOL
;
592 controls
= gh_cons (ly_offset2scm (b
.control_
[i
]), controls
);
595 All these null control-points, where do they all come from?
597 if (i
&& b
.control_
[i
][X_AXIS
] == 0)
604 me
->set_grob_property ("control-points", controls
);
608 Slur::get_curve (Grob
*me
)
613 if (!Directional_element_interface::get (me
)
614 || ! gh_symbol_p (index_cell (me
->get_grob_property ("attachment"), LEFT
))
615 || ! gh_symbol_p (index_cell (me
->get_grob_property ("attachment"), RIGHT
)))
616 set_extremities (me
);
618 if (!gh_pair_p (me
->get_grob_property ("control-points")))
619 set_control_points (me
);
621 // set_control_points may suicide
622 if (!scm_ilength (me
->get_grob_property ("note-columns")))
625 for (SCM s
= me
->get_grob_property ("control-points"); s
!= SCM_EOL
; s
= gh_cdr (s
))
627 b
.control_
[i
] = ly_scm2offset (gh_car (s
));
631 Array
<Offset
> enc (get_encompass_offset_arr (me
));
632 Direction dir
= Directional_element_interface::get (me
);
634 Real x1
= enc
[0][X_AXIS
];
635 Real x2
= enc
.top ()[X_AXIS
];
638 for (int i
=1; i
< enc
.size ()-1; i
++)
640 Real x
= enc
[i
][X_AXIS
];
643 Real y
= b
.get_other_coordinate (X_AXIS
, x
);
644 off
= off
>? dir
* (enc
[i
][Y_AXIS
] - y
);
647 b
.translate (Offset (0, dir
* off
));
653 Slur::has_interface (Grob
*me
)
655 return me
->has_interface (ly_symbol2scm ("slur-interface"));