2 tie.cc -- implement Tie
4 source file of the GNU LilyPond music typesetter
6 (c) 1997--2000 Han-Wen Nienhuys <hanwen@cs.uu.nl>
13 #include "paper-def.hh"
15 #include "rhythmic-head.hh"
17 #include "paper-column.hh"
19 #include "staff-symbol-referencer.hh"
20 #include "directional-element-interface.hh"
21 #include "molecule.hh"
22 #include "bezier-bow.hh"
24 #include "note-head.hh"
27 tie: Connect two noteheads.
31 c4 ~ \clef bass ; c4 or
35 do we have non-horizontal ties then?
40 Tie::set_head (Score_element
*me
,Direction d
, Item
* head_l
)
42 assert (!head (me
,d
));
43 index_set_cell (me
->get_elt_property ("heads"), d
, head_l
->self_scm ());
45 dynamic_cast<Spanner
*> (me
)->set_bound (d
, head_l
);
46 me
->add_dependency (head_l
);
50 Tie::set_interface (Score_element
*me
)
52 me
->set_elt_property ("heads", gh_cons (SCM_EOL
, SCM_EOL
));
53 me
->set_interface (ly_symbol2scm ("tie-interface"));
57 Tie::has_interface (Score_element
*me
)
59 return me
->has_interface (ly_symbol2scm ("tie-interface"));
63 Tie::head (Score_element
*me
, Direction d
)
65 SCM c
= me
->get_elt_property ("heads");
66 c
= index_cell (c
, d
);
68 return unsmob_element (c
);
72 Tie::position_f (Score_element
*me
)
74 Direction d
= head (me
,LEFT
) ? LEFT
:RIGHT
;
75 return Staff_symbol_referencer::position_f (head (me
,d
));
80 Default: Put the tie oppositie of the stem [Wanske p231]
82 In case of chords: Tie_column takes over
84 The direction of the Tie is more complicated (See [Ross] p136 and
88 Tie::get_default_dir (Score_element
*me
)
90 Item
* sl
= head(me
,LEFT
) ? Rhythmic_head::stem_l (head (me
,LEFT
)) :0;
91 Item
* sr
= head(me
,RIGHT
) ? Rhythmic_head::stem_l (head (me
,RIGHT
)) :0;
95 if (Directional_element_interface::get (sl
) == UP
96 && Directional_element_interface::get (sr
) == UP
)
101 Item
*s
= sl
? sl
: sr
;
102 return - Directional_element_interface::get (s
);
111 Tie::get_control_points (SCM smob
)
113 Spanner
*me
= dynamic_cast<Spanner
*> (unsmob_element (smob
));
114 Direction headdir
= CENTER
;
117 else if (head(me
,RIGHT
))
121 programming_error ("Tie without heads.");
123 return SCM_UNSPECIFIED
;
126 if (!Directional_element_interface::get (me
))
127 Directional_element_interface::set (me
, Tie::get_default_dir (me
));
129 Real staff_space
= Staff_symbol_referencer::staff_space (me
);
131 Real x_gap_f
= gh_scm2double (me
->get_elt_property ("x-gap"));
133 Score_element
* l
= me
->get_bound (LEFT
);
134 Score_element
* r
= me
->get_bound (RIGHT
);
136 Score_element
* commonx
= me
->common_refpoint (l
, X_AXIS
);
137 commonx
= me
->common_refpoint (r
, X_AXIS
);
142 this is a kludge: the tie has to be long enough to be
143 visible, but should not go through key sigs.
149 if (Note_head::has_interface (me
->get_bound (LEFT
)))
150 left_x
= l
->extent (l
, X_AXIS
)[RIGHT
] + x_gap_f
;
152 left_x
= l
->extent (l
, X_AXIS
).linear_combination (lambda
);
156 if (Note_head::has_interface (me
->get_bound (LEFT
))
157 && Note_head::has_interface (me
->get_bound (RIGHT
)))
160 + r
->extent (commonx
,X_AXIS
)[LEFT
]
161 - l
->extent (commonx
, X_AXIS
)[RIGHT
]
166 if (Note_head::has_interface (me
->get_bound (LEFT
)))
167 width
= r
->relative_coordinate (commonx
, X_AXIS
)
168 - l
->extent (commonx
, X_AXIS
)[RIGHT
]
172 - l
->extent (commonx
, X_AXIS
).linear_combination (lambda
)
173 + r
->extent (commonx
, X_AXIS
)[LEFT
]
177 Direction dir
= Directional_element_interface::get(me
);
179 SCM details
= me
->get_elt_property ("details");
181 SCM lim
// groetjes aan de chirurgendochter.
182 = scm_assq (ly_symbol2scm ("height-limit"),details
);
184 Real h_inf
= gh_scm2double (gh_cdr (lim
)) * staff_space
;
185 Real r_0
= gh_scm2double (gh_cdr (scm_assq (ly_symbol2scm ("ratio"),details
)));
187 Bezier b
= slur_shape (width
, h_inf
, r_0
);
189 Offset leave_dir
= b
.control_
[1] - b
.control_
[0];
191 Score_element
*hed
=head (me
, headdir
);
192 Real dx
= (hed
->extent (hed
, X_AXIS
).length () + x_gap_f
)/2.0;
193 Real max_gap
= leave_dir
[Y_AXIS
] * dx
/ leave_dir
[X_AXIS
];
196 for small ties (t small) we want to start in the Y-center (so dy = 0), for
197 large ties, the tie should appear to come from the center of the
198 head, so dy = max_gap
200 maybe use a different formula?
202 TODO: what if 2 heads have different size.
204 TODO: for small ties, it is better to start over the heads
205 iso. next to the heads.
207 Real t
= (width
/ staff_space
- 5.0); // ugh.
208 Real dy
= t
> 0 ? max_gap
* sqr (t
/ (1 + t
)) : 0.0;
210 Real ypos
= Tie::position_f (me
) * staff_space
/2 + dir
* dy
;
213 todo: prevent ending / staffline collision.
215 todo: tie / stem collision
218 b
= slur_shape(width
,h_inf
, r_0
);
220 b
.translate (Offset (left_x
, ypos
));
224 Avoid colliding of the horizontal part with stafflines.
227 TODO: redo this, heuristic is half-baken, and ties often look ugly
230 TODO: doesn't work when on staff with even number of lines.
232 Array
<Real
> horizontal (b
.solve_derivative (Offset (1,0)));
233 if (horizontal
.size ())
236 ugh. Doesnt work for non-horizontal curves.
238 Real y
= b
.curve_point (horizontal
[0])[Y_AXIS
];
240 Real ry
= rint (y
/staff_space
) * staff_space
;
244 Real clear
= staff_space
* gh_scm2double (me
->get_elt_property ("staffline-clearance"));
246 if (fabs (y
) <= Staff_symbol_referencer::staff_radius (me
)
247 && fabs (diff
) < clear
)
249 Real y1
= ry
+ clear
;
250 Real y2
= ry
- clear
;
252 newy
= (fabs (y1
- y
) < fabs (y2
- y
)) ? y1
: y2
;
254 // newy = ry - 0.5 * staff_space * sign (diff) ;
257 we don't want horizontal ties
259 if (fabs (newy
- b
.control_
[0][Y_AXIS
]) < 1e-2)
261 newy
= newy
+ dir
* staff_space
;
265 Real y0
= b
.control_
[0][Y_AXIS
];
266 b
.control_
[2][Y_AXIS
] =
267 b
.control_
[1][Y_AXIS
] =
268 (b
.control_
[1][Y_AXIS
] - y0
) * ((newy
- y0
) / (y
- y0
)) + y0
;
271 programming_error ("Tie is nowhere horizontal");
275 SCM controls
= SCM_EOL
;
277 controls
= gh_cons ( ly_offset2scm (b
.control_
[i
]), controls
);
281 MAKE_SCHEME_CALLBACK(Tie
,set_spacing_rods
,1);
284 TODO: set minimum distances for begin/end of line
287 Tie::set_spacing_rods (SCM smob
)
289 Score_element
*me
= unsmob_element (smob
);
290 Spanner
*sp
= dynamic_cast<Spanner
*> (me
);
293 r
.item_l_drul_
[LEFT
]=sp
->get_bound (LEFT
);
294 r
.item_l_drul_
[RIGHT
]=sp
->get_bound (RIGHT
);
297 = gh_scm2double (me
->get_elt_property ("minimum-length"))
298 * me
->paper_l ()->get_var ("staffspace");
300 return SCM_UNSPECIFIED
;
303 MAKE_SCHEME_CALLBACK(Tie
,brew_molecule
,1);
305 Tie::brew_molecule (SCM smob
)
307 Score_element
*me
= unsmob_element (smob
);
309 SCM cp
= me
->get_elt_property ("control-points");
312 cp
= get_control_points (smob
);
313 me
->set_elt_property ("control-points", cp
);
317 gh_scm2double (me
->get_elt_property ("thickness"))
318 * me
->paper_l ()->get_var ("stafflinethickness");
322 for (SCM s
= cp
; s
!= SCM_EOL
; s
= gh_cdr (s
))
324 b
.control_
[i
] = ly_scm2offset (gh_car (s
));
328 Molecule a
= Lookup::slur (b
, Directional_element_interface::get (me
) * thick
, thick
);
330 return a
.create_scheme ();