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?
41 Tie::set_head (Grob*me, Direction d, Grob * h)
43 assert (!head (me, d));
44 index_set_cell (me->get_property ("head-pair"), d, h->self_scm ());
46 dynamic_cast<Spanner*> (me)->set_bound (d, h);
47 me->add_dependency (h);
51 Tie::set_interface (Grob*me)
53 me->set_property ("head-pair", scm_cons (SCM_EOL, SCM_EOL));
58 Tie::head (Grob*me, Direction d)
60 SCM c = me->get_property ("head-pair");
63 return unsmob_grob (index_get_cell (c, d));
69 Tie::get_column_rank (Grob *me, Direction d)
71 Spanner *span = dynamic_cast<Spanner*> (me);
72 Grob * h = head (me, d);
74 h = span->get_bound (d);
76 Grob *col = dynamic_cast<Item*> (h)->get_column ();
77 return Paper_column::get_rank (col);
81 Tie::get_position (Grob*me)
83 Direction d = head (me, LEFT) ? LEFT:RIGHT;
84 return Staff_symbol_referencer::get_position (head (me, d));
89 Default: Put the tie oppositie of the stem [Wanske p231]
91 In case of chords: Tie_column takes over
93 The direction of the Tie is more complicated (See [Ross] p136 and
96 (what about linebreaks? )
100 Tie::get_default_dir (Grob*me)
102 Item * sl = head (me, LEFT) ? Rhythmic_head::get_stem (head (me, LEFT)) :0;
103 Item * sr = head (me, RIGHT) ? Rhythmic_head::get_stem (head (me, RIGHT)) :0;
106 if (get_grob_direction (sl) == UP
107 && get_grob_direction (sr) == UP)
112 Item *s = sl ? sl : sr;
113 return - get_grob_direction (s);
122 Tie::set_direction (Grob*me)
124 if (!get_grob_direction (me))
126 if (Tie_column::has_interface (me->get_parent (Y_AXIS)))
127 Tie_column::set_directions (me->get_parent (Y_AXIS));
129 set_grob_direction (me, Tie::get_default_dir (me));
134 TODO: we should also use thickness for computing the clearance
135 between head and tie. Very thick ties will now touch the note head.
139 Tie::get_control_points (SCM smob)
141 Spanner*me = unsmob_spanner (smob);
142 Direction headdir = CENTER;
145 else if (head (me, RIGHT))
149 programming_error ("Tie without heads.");
156 Direction dir = get_grob_direction (me);
158 Real staff_space = Staff_symbol_referencer::staff_space (me);
160 Real x_gap_f = robust_scm2double (me->get_property ("x-gap"), 0);
162 Grob* l = me->get_bound (LEFT);
163 Grob* r = me->get_bound (RIGHT);
165 Grob* commonx = me->common_refpoint (l, X_AXIS);
166 commonx = me->common_refpoint (r, X_AXIS);
171 the tie has to be long enough to be visible, but should not go
172 through key sigs. In the 1.5 series the pref.matter - note
173 distance is fixed , so this won't be a problem anymore.
177 if (Note_head::has_interface (l))
182 This correction is due te the shape of the black note head.
184 if (Rhythmic_head::duration_log (l) == 2)
186 left_x = l->extent (l, X_AXIS).linear_combination (where)
190 left_x = l->extent (l, X_AXIS).linear_combination (lambda);
194 if (Note_head::has_interface (l) && Note_head::has_interface (r))
197 + r->extent (commonx, X_AXIS)[LEFT]
198 - l->extent (commonx, X_AXIS)[RIGHT]
203 if (Note_head::has_interface (l))
204 width = r->relative_coordinate (commonx, X_AXIS)
205 - l->extent (commonx, X_AXIS)[RIGHT]
209 - l->extent (commonx, X_AXIS).linear_combination (lambda)
210 + r->extent (commonx, X_AXIS)[LEFT]
216 SCM details = me->get_property ("details");
218 SCM lim // groetjes aan de chirurgendochter.
219 = scm_assq (ly_symbol2scm ("height-limit"), details);
221 Real h_inf = scm_to_double (scm_cdr (lim)) * staff_space;
222 Real r_0 = scm_to_double (scm_cdr (scm_assq (ly_symbol2scm ("ratio"), details)));
224 Bezier b = slur_shape (width, h_inf, r_0);
227 I think this better, particularly for small ties. It always allows the user to move ties if
228 they seem in the wrong place
230 TODO: what if 2 heads have different size.
234 Real ypos = Tie::get_position (me) * staff_space/2
235 + dir * scm_to_double (me->get_property ("y-offset"));;
238 Make sure we don't start on a dots
240 if (Note_head::has_interface (l) && Rhythmic_head::get_dots (l))
242 Grob* dots = Rhythmic_head::get_dots (l);
243 if (fabs (staff_space * Staff_symbol_referencer::get_position (dots) /2
252 todo: prevent ending / staffline collision.
254 todo: tie / stem collision
257 b = slur_shape (width, h_inf, r_0);
259 b.translate (Offset (left_x, ypos));
263 Avoid colliding of the horizontal part with stafflines.
266 TODO: redo this, heuristic is half-baken, and ties often look ugly
269 TODO: doesn't work when on staff with even number of lines.
271 Array<Real> horizontal (b.solve_derivative (Offset (1, 0)));
272 if (horizontal.size ())
275 ugh. Doesnt work for non-horizontal curves.
277 Real y = b.curve_point (horizontal[0])[Y_AXIS];
279 Real ry = rint (y/staff_space) * staff_space;
283 Real clear = staff_space * scm_to_double (me->get_property ("staffline-clearance"));
286 Staff_symbol_referencer::staff_radius (me) * staff_space + clear
287 && fabs (diff) < clear)
289 Real y1 = ry + clear;
290 Real y2 = ry - clear;
293 ugh, we shove the 0.5 out of our sleeves.
295 Any way. This test is to make sure that staffline
296 collision avoidance does not result in completely flat
299 if (fabs (y1 - ypos) < 0.5)
301 else if (fabs (y2 - ypos) < 0.5)
304 newy = (fabs (y1 - y) < fabs (y2 - y)) ? y1 : y2;
306 // newy = ry - 0.5 * staff_space * sign (diff) ;
309 we don't want horizontal ties
311 if (fabs (newy - b.control_[0][Y_AXIS]) < 1e-2)
313 newy = newy + dir * staff_space;
317 Real y0 = b.control_ [0][Y_AXIS];
318 b.control_[2][Y_AXIS] =
319 b.control_[1][Y_AXIS] =
320 (b.control_[1][Y_AXIS] - y0) * ((newy - y0) / (y - y0)) + y0;
323 programming_error ("Tie is nowhere horizontal");
327 SCM controls = SCM_EOL;
328 for (int i = 4; i--;)
329 controls = scm_cons (ly_offset2scm (b.control_[i]), controls);
334 MAKE_SCHEME_CALLBACK (Tie, print, 1);
336 Tie::print (SCM smob)
338 Grob*me = unsmob_grob (smob);
340 SCM cp = me->get_property ("control-points");
341 if (!scm_is_pair (cp)) // list is more accurate
343 cp = get_control_points (smob);
344 me->set_property ("control-points", cp);
347 if (!scm_is_pair (cp))
348 return Stencil ().smobbed_copy ();
351 = Staff_symbol_referencer::line_thickness (me)
352 * robust_scm2double (me->get_property ("thickness"), 1);
356 for (SCM s = cp; s != SCM_EOL; s = scm_cdr (s))
358 b.control_[i] = ly_scm2offset (scm_car (s));
362 Stencil a = Lookup::slur (b, get_grob_direction (me) * thick, thick);
364 return a.smobbed_copy ();
369 ADD_INTERFACE (Tie, "tie-interface",
370 "A tie connecting two noteheads.\n"
372 "y-offset staffline-clearance control-points head-pair details thickness x-gap direction minimum-length");