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
239 todo: prevent ending / staffline collision.
241 todo: tie / stem collision
244 b = slur_shape (width, h_inf, r_0);
246 b.translate (Offset (left_x, ypos));
249 Avoid colliding of the horizontal part with stafflines.
252 TODO: redo this, heuristic is half-baken, and ties often look ugly
255 TODO: doesn't work when on staff with even number of lines.
257 Array<Real> horizontal (b.solve_derivative (Offset (1, 0)));
258 if (horizontal.size ())
261 ugh. Doesnt work for non-horizontal curves.
263 Real y = b.curve_point (horizontal[0])[Y_AXIS];
265 Real ry = rint (y / staff_space) * staff_space;
269 Real clear = staff_space * scm_to_double (me->get_property ("staffline-clearance"));
272 <= Staff_symbol_referencer::staff_radius (me) * staff_space + clear
273 && fabs (diff) < clear)
275 Real y1 = ry + clear;
276 Real y2 = ry - clear;
279 ugh, we shove the 0.5 out of our sleeves.
281 Any way. This test is to make sure that staffline
282 collision avoidance does not result in completely flat
285 if (fabs (y1 - ypos) < 0.5)
287 else if (fabs (y2 - ypos) < 0.5)
290 newy = (fabs (y1 - y) < fabs (y2 - y)) ? y1 : y2;
292 // newy = ry - 0.5 * staff_space * sign (diff) ;
295 we don't want horizontal ties
297 if (fabs (newy - b.control_[0][Y_AXIS]) < 1e-2)
298 newy = newy + dir * staff_space;
301 Real y0 = b.control_ [0][Y_AXIS];
302 b.control_[2][Y_AXIS]
303 = b.control_[1][Y_AXIS]
304 = (b.control_[1][Y_AXIS] - y0) * ((newy - y0) / (y - y0)) + y0;
307 programming_error ("tie is nowhere horizontal");
309 SCM controls = SCM_EOL;
310 for (int i = 4; i--;)
311 controls = scm_cons (ly_offset2scm (b.control_[i]), controls);
315 MAKE_SCHEME_CALLBACK (Tie, print, 1);
317 Tie::print (SCM smob)
319 Grob *me = unsmob_grob (smob);
321 SCM cp = me->get_property ("control-points");
322 if (!scm_is_pair (cp)) // list is more accurate
324 cp = get_control_points (smob);
325 me->set_property ("control-points", cp);
328 if (!scm_is_pair (cp))
329 return Stencil ().smobbed_copy ();
331 Real staff_thick = Staff_symbol_referencer::line_thickness (me);
332 Real base_thick = robust_scm2double (me->get_property ("thickness"), 1);
333 Real thick = base_thick * staff_thick;
337 for (SCM s = cp; s != SCM_EOL; s = scm_cdr (s))
339 b.control_[i] = ly_scm2offset (scm_car (s));
345 SCM p = me->get_property ("dash-period");
346 SCM f = me->get_property ("dash-fraction");
347 if (scm_is_number (p) && scm_is_number (f))
348 a = Lookup::dashed_slur (b,
350 robust_scm2double (p, 1.0),
351 robust_scm2double (f, 0));
354 get_grob_direction (me) * staff_thick,
357 return a.smobbed_copy ();
362 "A tie connecting two noteheads.\n",
364 "y-offset dash-period dash-fraction "
365 "staffline-clearance control-points head-pair "
366 "details thickness x-gap direction minimum-length");