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 * should avoid stafflines with horizontal part.
14 * begin and end should be treated as a/acknowledge Scripts.
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 (Grob
*me
)
40 /* Ugh, junked this function, but if we don't do this, we somehow
41 won't be able to write to it */
42 me
->set_grob_property ("attachment", me
->get_grob_property ("attachment"));
46 Slur::add_column (Grob
*me
, Grob
*n
)
48 if (!gh_pair_p (n
->get_grob_property ("note-heads")))
49 warning (_ ("Putting slur over rest. Ignoring."));
52 Pointer_group_interface::add_element (me
, "note-columns",n
);
53 me
->add_dependency (n
);
56 add_bound_item (dynamic_cast<Spanner
*> (me
), dynamic_cast<Item
*>(n
));
60 Slur::de_uglyfy (Grob
*me
, Slur_bezier_bow
* bb
, Real default_height
)
62 Real length
= bb
->curve_
.control_
[3][X_AXIS
] ;
63 Real ff
= bb
->fit_factor ();
64 for (int i
= 1; i
< 3; i
++)
66 Real ind
= abs (bb
->curve_
.control_
[(i
-1)*3][X_AXIS
]
67 - bb
->curve_
.control_
[i
][X_AXIS
]) / length
;
68 Real h
= bb
->curve_
.control_
[i
][Y_AXIS
] * ff
/ length
;
70 Real f
= default_height
/ length
;
71 SCM up
= me
->get_grob_property ("de-uglify-parameters");
73 Real c1
= gh_scm2double (gh_car (up
));
74 Real c2
= gh_scm2double (gh_cadr (up
));
75 Real c3
= gh_scm2double (gh_caddr (up
));
81 else if (h
> c2
+ c3
* ind
)
86 bb
->curve_
.control_
[i
][Y_AXIS
] = h
* length
;
89 bb
->curve_
.assert_sanity ();
93 Slur::get_default_dir (Grob
*me
)
95 Link_array
<Grob
> encompass_arr
=
96 Pointer_group_interface__extract_elements (me
, (Grob
*)0, "note-columns");
99 for (int i
=0; i
< encompass_arr
.size (); i
++)
101 if (Note_column::dir (encompass_arr
[i
]) < 0)
111 MAKE_SCHEME_CALLBACK (Slur
, after_line_breaking
,1);
113 Slur::after_line_breaking (SCM smob
)
115 Grob
*me
= unsmob_grob (smob
);
116 if (!scm_ilength (me
->get_grob_property ("note-columns")))
119 return SCM_UNSPECIFIED
;
121 set_extremities (me
);
122 set_control_points (me
);
123 return SCM_UNSPECIFIED
;
128 Slur::check_slope (Grob
*me
)
131 Avoid too steep slurs.
133 SCM s
= me
->get_grob_property ("slope-limit");
136 Array
<Offset
> encompass
= get_encompass_offset_arr (me
);
137 Drul_array
<Offset
> attachment
;
138 attachment
[LEFT
] = encompass
[0];
139 attachment
[RIGHT
] = encompass
.top ();
141 Real dx
= attachment
[RIGHT
][X_AXIS
] - attachment
[LEFT
][X_AXIS
];
142 Real dy
= attachment
[RIGHT
][Y_AXIS
] - attachment
[LEFT
][Y_AXIS
];
146 Real slope
= slope
= abs (dy
/ dx
);
148 Real limit
= gh_scm2double (s
);
152 Real staff_space
= Staff_symbol_referencer::staff_space ((Grob
*)me
);
153 Direction dir
= (Direction
)gh_scm2int (me
->get_grob_property ("direction"));
154 Direction d
= (Direction
)(- dir
* (sign (dy
)));
155 SCM a
= me
->get_grob_property ("attachment-offset");
156 Drul_array
<Offset
> o
;
157 o
[LEFT
] = ly_scm2offset (index_cell (a
, LEFT
));
158 o
[RIGHT
] = ly_scm2offset (index_cell (a
, RIGHT
));
159 o
[d
][Y_AXIS
] -= (limit
- slope
) * dx
* dir
/ staff_space
;
160 //o[d][Y_AXIS] = attachment[-d][Y_AXIS] + (dx * limit * dir / staff_space);
161 me
->set_grob_property ("attachment-offset",
162 gh_cons (ly_offset2scm (o
[LEFT
]),
163 ly_offset2scm (o
[RIGHT
])));
169 Slur::set_extremities (Grob
*me
)
171 if (!Directional_element_interface::get (me
))
172 Directional_element_interface::set (me
, get_default_dir (me
));
174 Direction dir
= LEFT
;
177 if (!gh_symbol_p (index_cell (me
->get_grob_property ("attachment"), dir
)))
179 for (SCM s
= me
->get_grob_property ("extremity-rules");
180 s
!= SCM_EOL
; s
= gh_cdr (s
))
182 SCM r
= gh_call2 (gh_caar (s
), me
->self_scm (),
183 gh_int2scm ((int)dir
));
186 index_set_cell (me
->get_grob_property ("attachment"), dir
,
193 while (flip (&dir
) != LEFT
);
200 Slur::get_first_notecolumn_y (Grob
*me
, Direction dir
)
202 Grob
*col
= dir
== LEFT
203 ? unsmob_grob (gh_car (scm_reverse (me
->get_grob_property
206 (gh_car (me
->get_grob_property ("note-columns")));
211 me
->common_refpoint (col
, Y_AXIS
)
214 if (col
== ((Spanner
*)me
)->get_bound (dir
))
216 y
= get_attachment (me
, dir
, common
)[Y_AXIS
];
220 y
= encompass_offset (me
, col
, common
)[Y_AXIS
]
221 - me
->relative_coordinate (common
[Y_AXIS
], Y_AXIS
);
227 Slur::broken_trend_offset (Grob
*me
, Direction dir
)
230 A broken slur should maintain the same vertical trend
231 the unbroken slur would have had.
234 if (Spanner
*mother
= dynamic_cast<Spanner
*> (me
->original_l_
))
236 for (int i
= dir
== LEFT
? 0 : mother
->broken_into_l_arr_
.size () - 1;
237 dir
== LEFT
? i
< mother
->broken_into_l_arr_
.size () : i
> 0;
238 dir
== LEFT
? i
++ : i
--)
240 if (mother
->broken_into_l_arr_
[i
- dir
] == me
)
242 Grob
*neighbour
= mother
->broken_into_l_arr_
[i
];
244 neighbour
->set_grob_property ("direction",
245 me
->get_grob_property ("direction"));
246 Real neighbour_y
= get_first_notecolumn_y (neighbour
, dir
);
247 Real y
= get_first_notecolumn_y (me
, -dir
);
248 int neighbour_cols
= scm_ilength (neighbour
->get_grob_property ("note-columns"));
249 int cols
= scm_ilength (me
->get_grob_property ("note-columns"));
250 o
= Offset (0, (y
*neighbour_cols
+ neighbour_y
*cols
) /
251 (cols
+ neighbour_cols
));
260 Slur::get_attachment (Grob
*me
, Direction dir
,
263 SCM s
= me
->get_grob_property ("attachment");
264 if (!gh_symbol_p (index_cell (s
, dir
)))
266 set_extremities (me
);
267 s
= me
->get_grob_property ("attachment");
269 SCM a
= dir
== LEFT
? gh_car (s
) : gh_cdr (s
);
270 Spanner
*sp
= dynamic_cast<Spanner
*>(me
);
271 String str
= ly_symbol2string (a
);
272 Real staff_space
= Staff_symbol_referencer::staff_space ((Grob
*)me
);
273 Real hs
= staff_space
/ 2.0;
277 if (Note_column::has_interface (sp
->get_bound (dir
)))
279 Grob
* n
=sp
->get_bound (dir
);
280 if ((stem
= Note_column::stem_l (n
)))
285 o
= Offset (0, Stem::head_positions (stem
)
286 [Directional_element_interface::get (me
)] * hs
);
288 Default position is centered in X, on outer side of head Y
290 o
+= Offset (0.5 * n
->extent (n
,X_AXIS
).length (),
292 * Directional_element_interface::get (me
));
294 else if (str
== "alongside-stem")
296 o
= Offset (0, Stem::chord_start_f (stem
));
298 Default position is on stem X, on outer side of head Y
300 o
+= Offset (n
->extent (n
,X_AXIS
).length ()
301 * (1 + Stem::get_direction (stem
)),
303 * Directional_element_interface::get (me
));
305 else if (str
== "stem")
307 o
= Offset (0, Stem::stem_end_position (stem
) * hs
);
309 Default position is on stem X, at stem end Y
312 (n
->extent (n
,X_AXIS
).length ()
313 - stem
->extent (stem
,X_AXIS
).length ())
314 * (1 + Stem::get_direction (stem
)),
319 else if (str
== "loose-end")
321 SCM other_a
= dir
== LEFT
? gh_cdr (s
) : gh_car (s
);
322 if (ly_symbol2string (other_a
) != "loose-end")
326 The braindead way: horizontal
328 o
= Offset (0, get_attachment (me
, -dir
, common
)[Y_AXIS
]);
330 o
= broken_trend_offset (me
, dir
);
338 SCM alist
= me
->get_grob_property ("extremity-offset-alist");
339 int stemdir
= stem
? Stem::get_direction (stem
) : 1;
340 int slurdir
= gh_scm2int (me
->get_grob_property ("direction"));
343 gh_int2scm (stemdir
* dir
),
344 gh_int2scm (slurdir
* dir
),
345 SCM_UNDEFINED
), alist
);
349 o
+= ly_scm2offset (gh_cdr (l
)) * staff_space
* dir
;
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 o
+= ly_scm2offset (index_cell (me
->get_grob_property ("attachment-offset"),
368 Slur::encompass_offset (Grob
*me
,
373 Grob
* stem_l
= unsmob_grob (col
->get_grob_property ("stem"));
375 Direction dir
= Directional_element_interface::get (me
);
379 warning (_ ("Slur over rest?"));
380 o
[X_AXIS
] = col
->relative_coordinate (common
[X_AXIS
], X_AXIS
);
381 o
[Y_AXIS
] = col
->relative_coordinate (common
[Y_AXIS
], Y_AXIS
);
384 Direction stem_dir
= Directional_element_interface::get (stem_l
);
385 o
[X_AXIS
] = stem_l
->relative_coordinate (0, X_AXIS
);
388 Simply set x to middle of notehead
391 o
[X_AXIS
] -= 0.5 * stem_dir
* col
->extent (col
,X_AXIS
).length ();
393 if ((stem_dir
== dir
)
394 && !stem_l
->extent (stem_l
, Y_AXIS
).empty_b ())
396 o
[Y_AXIS
] = stem_l
->extent (common
[Y_AXIS
], Y_AXIS
)[dir
];
400 o
[Y_AXIS
] = col
->extent (common
[Y_AXIS
], Y_AXIS
)[dir
];
404 leave a gap: slur mustn't touch head/stem
406 o
[Y_AXIS
] += dir
* gh_scm2double (me
->get_grob_property ("y-free")) *
412 Slur::get_encompass_offset_arr (Grob
*me
)
414 Spanner
*sp
= dynamic_cast<Spanner
*>(me
);
415 SCM eltlist
= me
->get_grob_property ("note-columns");
416 Grob
*common
[] = {me
->common_refpoint (eltlist
, X_AXIS
),
417 me
->common_refpoint (eltlist
, Y_AXIS
)};
420 common
[X_AXIS
] = common
[X_AXIS
]->common_refpoint (sp
->get_bound (RIGHT
), X_AXIS
);
421 common
[X_AXIS
] = common
[X_AXIS
]->common_refpoint (sp
->get_bound (LEFT
), X_AXIS
);
423 Link_array
<Grob
> encompass_arr
;
424 while (gh_pair_p (eltlist
))
426 encompass_arr
.push (unsmob_grob (gh_car (eltlist
)));
427 eltlist
=gh_cdr (eltlist
);
429 encompass_arr
.reverse ();
432 Array
<Offset
> offset_arr
;
434 Offset
origin (me
->relative_coordinate (common
[X_AXIS
], X_AXIS
),
435 me
->relative_coordinate (common
[Y_AXIS
], Y_AXIS
));
438 int last
= encompass_arr
.size () - 2;
440 offset_arr
.push (get_attachment (me
, LEFT
, common
));
446 if (encompass_arr
[0] != sp
->get_bound (LEFT
))
451 offset_arr
[0][Y_AXIS
] -=
452 encompass_arr
[0]->relative_coordinate (common
[Y_AXIS
], Y_AXIS
)
453 - me
->relative_coordinate (common
[Y_AXIS
], Y_AXIS
);
459 if (encompass_arr
.top () != sp
->get_bound (RIGHT
))
464 for (int i
= first
; i
<= last
; i
++)
466 Offset
o (encompass_offset (me
, encompass_arr
[i
], common
));
467 offset_arr
.push (o
- origin
);
470 offset_arr
.push (Offset (sp
->spanner_length (), 0) + get_attachment (me
, RIGHT
,common
));
472 if (encompass_arr
[0] != sp
->get_bound (LEFT
))
474 offset_arr
.top ()[Y_AXIS
] -= encompass_arr
.top ()->relative_coordinate (common
[Y_AXIS
], Y_AXIS
)
475 - me
->relative_coordinate (common
[Y_AXIS
], Y_AXIS
);
482 MAKE_SCHEME_CALLBACK(Slur
,set_spacing_rods
,1);
484 Slur::set_spacing_rods (SCM smob
)
486 Grob
*me
= unsmob_grob (smob
);
489 Spanner
*sp
= dynamic_cast<Spanner
*>(me
);
490 r
.item_l_drul_
[LEFT
] = sp
->get_bound (LEFT
);
491 r
.item_l_drul_
[RIGHT
] = sp
->get_bound (RIGHT
);
493 gh_scm2double (me
->get_grob_property ("minimum-length"))
497 return SCM_UNSPECIFIED
;
504 MAKE_SCHEME_CALLBACK (Slur
, height
, 2);
506 Slur::height (SCM smob
, SCM ax
)
508 Axis a
= (Axis
)gh_scm2int (ax
);
509 Grob
* me
= unsmob_grob (smob
);
510 assert ( a
== Y_AXIS
);
512 SCM mol
= me
->get_uncached_molecule ();
513 return ly_interval2scm (unsmob_molecule (mol
)->extent (a
));
517 Ugh should have dash-length + dash-period
519 MAKE_SCHEME_CALLBACK (Slur
, brew_molecule
,1);
521 Slur::brew_molecule (SCM smob
)
523 Grob
* me
= unsmob_grob (smob
);
524 if (!scm_ilength (me
->get_grob_property ("note-columns")))
530 Real thick
= me
->paper_l ()->get_var ("stafflinethickness") *
531 gh_scm2double (me
->get_grob_property ("thickness"));
532 Bezier one
= get_curve (me
);
534 // get_curve may suicide
535 if (!scm_ilength (me
->get_grob_property ("note-columns")))
539 SCM d
= me
->get_grob_property ("dashed");
541 a
= Lookup::dashed_slur (one
, thick
, thick
* gh_scm2double (d
));
543 a
= Lookup::slur (one
, Directional_element_interface::get (me
) * thick
, thick
);
545 return a
.smobbed_copy();
549 Slur::set_control_points (Grob
*me
)
551 Real staff_space
= Staff_symbol_referencer::staff_space ((Grob
*)me
);
553 SCM details
= me
->get_grob_property ("details");
554 SCM h_inf_scm
= scm_assq (ly_symbol2scm ("height-limit"), details
);
555 SCM r_0_scm
= scm_assq (ly_symbol2scm ("ratio"), details
);
557 Real r_0
= gh_scm2double (gh_cdr (r_0_scm
));
558 Real h_inf
= staff_space
* gh_scm2double (gh_cdr (h_inf_scm
));
560 Slur_bezier_bow
bb (get_encompass_offset_arr (me
),
561 Directional_element_interface::get (me
),
564 if (bb
.fit_factor () > 1.0)
566 Real length
= bb
.curve_
.control_
[3][X_AXIS
];
567 Real default_height
= slur_height (length
, h_inf
, r_0
);
569 SCM ssb
= me
->get_grob_property ("beautiful");
571 if (gh_number_p (ssb
))
572 sb
= gh_scm2double (ssb
);
574 bb
.minimise_enclosed_area ( sb
, details
);
575 SCM sbf
= scm_assq (ly_symbol2scm ("force-blowfit"), details
);
577 if (gh_pair_p (sbf
) && gh_number_p (gh_cdr (sbf
)))
578 bff
= gh_scm2double (gh_cdr (sbf
));
580 bb
.curve_
.control_
[1][Y_AXIS
] *= bff
;
581 bb
.curve_
.control_
[2][Y_AXIS
] *= bff
;
585 Real beautiful
= length
* default_height
* sb
;
586 Real area
= bb
.enclosed_area_f ();
589 Slurs that fit beautifully are not ugly
591 if (area
> beautiful
)
592 de_uglyfy (me
, &bb
, default_height
);
595 Bezier b
= bb
.get_bezier ();
598 SCM controls
= SCM_EOL
;
601 controls
= gh_cons ( ly_offset2scm (b
.control_
[i
]), controls
);
604 All these null control-points, where do they all come from?
606 if (i
&& b
.control_
[i
][X_AXIS
] == 0)
613 me
->set_grob_property ("control-points", controls
);
617 Slur::get_curve (Grob
*me
)
622 if (!Directional_element_interface::get (me
)
623 || ! gh_symbol_p (index_cell (me
->get_grob_property ("attachment"), LEFT
))
624 || ! gh_symbol_p (index_cell (me
->get_grob_property ("attachment"), RIGHT
)))
625 set_extremities (me
);
627 if (!gh_pair_p (me
->get_grob_property ("control-points")))
628 set_control_points (me
);
630 // set_control_points may suicide
631 if (!scm_ilength (me
->get_grob_property ("note-columns")))
634 for (SCM s
= me
->get_grob_property ("control-points"); s
!= SCM_EOL
; s
= gh_cdr (s
))
636 b
.control_
[i
] = ly_scm2offset (gh_car (s
));
640 Array
<Offset
> enc (get_encompass_offset_arr (me
));
641 Direction dir
= Directional_element_interface::get (me
);
643 Real x1
= enc
[0][X_AXIS
];
644 Real x2
= enc
.top ()[X_AXIS
];
647 for (int i
=1; i
< enc
.size ()-1; i
++)
649 Real x
= enc
[i
][X_AXIS
];
652 Real y
= b
.get_other_coordinate (X_AXIS
, x
);
653 off
= off
>? dir
* (enc
[i
][Y_AXIS
] - y
);
656 b
.translate (Offset (0, dir
* off
));
662 Slur::has_interface (Grob
*me
)
664 return me
->has_interface (ly_symbol2scm ("slur-interface"));