2 tie.cc -- implement Tie
4 source file of the GNU LilyPond music typesetter
6 (c) 1997--2005 Han-Wen Nienhuys <hanwen@cs.uu.nl>
15 #include "output-def.hh"
16 #include "rhythmic-head.hh"
18 #include "paper-column.hh"
20 #include "staff-symbol-referencer.hh"
21 #include "directional-element-interface.hh"
24 #include "note-head.hh"
25 #include "tie-column.hh"
28 tie: Connect two noteheads.
32 c4 ~ \clef bass ; c4 or
36 do we have non-horizontal ties then?
40 Tie::set_head (Grob *me, Direction d, Grob *h)
42 assert (!head (me, d));
43 index_set_cell (me->get_property ("head-pair"), d, h->self_scm ());
45 dynamic_cast<Spanner *> (me)->set_bound (d, h);
46 me->add_dependency (h);
50 Tie::set_interface (Grob *me)
52 me->set_property ("head-pair", scm_cons (SCM_EOL, SCM_EOL));
56 Tie::head (Grob *me, Direction d)
58 SCM c = me->get_property ("head-pair");
61 return unsmob_grob (index_get_cell (c, d));
67 Tie::get_column_rank (Grob *me, Direction d)
69 Spanner *span = dynamic_cast<Spanner *> (me);
70 Grob *h = head (me, d);
72 h = span->get_bound (d);
74 Grob *col = dynamic_cast<Item *> (h)->get_column ();
75 return Paper_column::get_rank (col);
79 Tie::get_position (Grob *me)
81 Direction d = head (me, LEFT) ? LEFT : RIGHT;
82 return Staff_symbol_referencer::get_position (head (me, d));
86 Default: Put the tie oppositie of the stem [Wanske p231]
88 In case of chords: Tie_column takes over
90 The direction of the Tie is more complicated (See [Ross] p136 and
93 (what about linebreaks? )
96 Tie::get_default_dir (Grob *me)
98 Item *sl = head (me, LEFT) ? Rhythmic_head::get_stem (head (me, LEFT)) : 0;
99 Item *sr = head (me, RIGHT) ? Rhythmic_head::get_stem (head (me, RIGHT)) : 0;
102 if (get_grob_direction (sl) == UP
103 && get_grob_direction (sr) == UP)
108 Item *s = sl ? sl : sr;
109 return -get_grob_direction (s);
116 Tie::set_direction (Grob *me)
118 if (!get_grob_direction (me))
120 if (Tie_column::has_interface (me->get_parent (Y_AXIS)))
121 Tie_column::set_directions (me->get_parent (Y_AXIS));
123 set_grob_direction (me, Tie::get_default_dir (me));
128 TODO: we should also use thickness for computing the clearance
129 between head and tie. Very thick ties will now touch the note head.
132 Tie::get_control_points (SCM smob)
134 Spanner *me = unsmob_spanner (smob);
135 Direction headdir = CENTER;
138 else if (head (me, RIGHT))
142 programming_error ("tie without heads");
149 Direction dir = get_grob_direction (me);
151 Real staff_space = Staff_symbol_referencer::staff_space (me);
153 Real x_gap_f = robust_scm2double (me->get_property ("x-gap"), 0);
155 Grob *l = me->get_bound (LEFT);
156 Grob *r = me->get_bound (RIGHT);
158 Grob *commonx = me->common_refpoint (l, X_AXIS);
159 commonx = me->common_refpoint (r, X_AXIS);
164 the tie has to be long enough to be visible, but should not go
165 through key sigs. In the 1.5 series the pref.matter - note
166 distance is fixed , so this won't be a problem anymore.
170 if (Note_head::has_interface (l))
175 This correction is due te the shape of the black note head.
177 if (Rhythmic_head::duration_log (l) == 2)
179 left_x = l->extent (l, X_AXIS).linear_combination (where)
183 left_x = l->extent (l, X_AXIS).linear_combination (lambda);
186 if (Note_head::has_interface (l) && Note_head::has_interface (r))
189 = + r->extent (commonx, X_AXIS)[LEFT]
190 - l->extent (commonx, X_AXIS)[RIGHT]
195 if (Note_head::has_interface (l))
196 width = r->relative_coordinate (commonx, X_AXIS)
197 - l->extent (commonx, X_AXIS)[RIGHT]
201 = -l->extent (commonx, X_AXIS).linear_combination (lambda)
202 + r->extent (commonx, X_AXIS)[LEFT]
206 SCM details = me->get_property ("details");
208 SCM lim // groetjes aan de chirurgendochter.
209 = scm_assq (ly_symbol2scm ("height-limit"), details);
211 Real h_inf = scm_to_double (scm_cdr (lim)) * staff_space;
212 Real r_0 = scm_to_double (scm_cdr (scm_assq (ly_symbol2scm ("ratio"), details)));
214 Bezier b = slur_shape (width, h_inf, r_0);
217 I think this better, particularly for small ties. It always allows the user to move ties if
218 they seem in the wrong place
220 TODO: what if 2 heads have different size.
224 Real ypos = Tie::get_position (me) * staff_space / 2
225 + dir * scm_to_double (me->get_property ("y-offset"));;
228 Make sure we don't start on a dots
230 if (Note_head::has_interface (l) && Rhythmic_head::get_dots (l))
232 Grob *dots = Rhythmic_head::get_dots (l);
233 if (fabs (staff_space * Staff_symbol_referencer::get_position (dots) / 2
241 todo: prevent ending / staffline collision.
243 todo: tie / stem collision
246 b = slur_shape (width, h_inf, r_0);
248 b.translate (Offset (left_x, ypos));
251 Avoid colliding of the horizontal part with stafflines.
254 TODO: redo this, heuristic is half-baken, and ties often look ugly
257 TODO: doesn't work when on staff with even number of lines.
259 Array<Real> horizontal (b.solve_derivative (Offset (1, 0)));
260 if (horizontal.size ())
263 ugh. Doesnt work for non-horizontal curves.
265 Real y = b.curve_point (horizontal[0])[Y_AXIS];
267 Real ry = rint (y / staff_space) * staff_space;
271 Real clear = staff_space * scm_to_double (me->get_property ("staffline-clearance"));
274 <= Staff_symbol_referencer::staff_radius (me) * staff_space + clear
275 && fabs (diff) < clear)
277 Real y1 = ry + clear;
278 Real y2 = ry - clear;
281 ugh, we shove the 0.5 out of our sleeves.
283 Any way. This test is to make sure that staffline
284 collision avoidance does not result in completely flat
287 if (fabs (y1 - ypos) < 0.5)
289 else if (fabs (y2 - ypos) < 0.5)
292 newy = (fabs (y1 - y) < fabs (y2 - y)) ? y1 : y2;
294 // newy = ry - 0.5 * staff_space * sign (diff) ;
297 we don't want horizontal ties
299 if (fabs (newy - b.control_[0][Y_AXIS]) < 1e-2)
301 newy = newy + dir * staff_space;
305 Real y0 = b.control_ [0][Y_AXIS];
306 b.control_[2][Y_AXIS]
307 = b.control_[1][Y_AXIS]
308 = (b.control_[1][Y_AXIS] - y0) * ((newy - y0) / (y - y0)) + y0;
311 programming_error ("tie is nowhere horizontal");
313 SCM controls = SCM_EOL;
314 for (int i = 4; i--;)
315 controls = scm_cons (ly_offset2scm (b.control_[i]), controls);
319 MAKE_SCHEME_CALLBACK (Tie, print, 1);
321 Tie::print (SCM smob)
323 Grob *me = unsmob_grob (smob);
325 SCM cp = me->get_property ("control-points");
326 if (!scm_is_pair (cp)) // list is more accurate
328 cp = get_control_points (smob);
329 me->set_property ("control-points", cp);
332 if (!scm_is_pair (cp))
333 return Stencil ().smobbed_copy ();
336 Real staff_thick = Staff_symbol_referencer::line_thickness (me);
337 Real base_thick = robust_scm2double (me->get_property ("thickness"), 1);
338 Real thick = base_thick * staff_thick;
343 for (SCM s = cp; s != SCM_EOL; s = scm_cdr (s))
345 b.control_[i] = ly_scm2offset (scm_car (s));
351 SCM p = me->get_property ("dash-period");
352 SCM f = me->get_property ("dash-fraction");
353 if (scm_is_number (p) && scm_is_number (f))
354 a = Lookup::dashed_slur (b,
356 robust_scm2double (p, 1.0),
357 robust_scm2double (f, 0));
360 get_grob_direction (me) * staff_thick,
364 return a.smobbed_copy ();
369 "A tie connecting two noteheads.\n",
371 "y-offset dash-period dash-fraction "
372 "staffline-clearance control-points head-pair "
373 "details thickness x-gap direction minimum-length");