2 tie.cc -- implement Tie
4 source file of the GNU LilyPond music typesetter
6 (c) 1997--2003 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"
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_grob_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_grob_property ("head-pair", gh_cons (SCM_EOL, SCM_EOL));
58 Tie::head (Grob*me, Direction d)
60 SCM c = me->get_grob_property ("head-pair");
61 c = index_get_cell (c, d);
63 return unsmob_grob (c);
67 Tie::get_position (Grob*me)
69 Direction d = head (me,LEFT) ? LEFT:RIGHT;
70 return Staff_symbol_referencer::get_position (head (me,d));
75 Default: Put the tie oppositie of the stem [Wanske p231]
77 In case of chords: Tie_column takes over
79 The direction of the Tie is more complicated (See [Ross] p136 and
82 (what about linebreaks? )
86 Tie::get_default_dir (Grob*me)
88 Item * sl = head (me,LEFT) ? Rhythmic_head::get_stem (head (me,LEFT)) :0;
89 Item * sr = head (me,RIGHT) ? Rhythmic_head::get_stem (head (me,RIGHT)) :0;
92 if (get_grob_direction (sl) == UP
93 && get_grob_direction (sr) == UP)
98 Item *s = sl ? sl : sr;
99 return - get_grob_direction (s);
108 Tie::set_direction (Grob*me)
110 if (!get_grob_direction (me))
112 if (Tie_column::has_interface (me->get_parent (Y_AXIS)))
113 Tie_column::set_directions (me->get_parent (Y_AXIS));
115 set_grob_direction (me, Tie::get_default_dir (me));
120 TODO: we should also use thickness for computing the clearance
121 between head and tie. Very thick ties will now touch the note head.
125 Tie::get_control_points (SCM smob)
127 Spanner*me = unsmob_spanner (smob);
128 Direction headdir = CENTER;
131 else if (head (me,RIGHT))
135 programming_error ("Tie without heads.");
137 return SCM_UNSPECIFIED;
142 Direction dir = get_grob_direction (me);
144 Real staff_space = Staff_symbol_referencer::staff_space (me);
146 Real x_gap_f = robust_scm2double (me->get_grob_property ("x-gap"), 0);
148 Grob* l = me->get_bound (LEFT);
149 Grob* r = me->get_bound (RIGHT);
151 Grob* commonx = me->common_refpoint (l, X_AXIS);
152 commonx = me->common_refpoint (r, X_AXIS);
157 the tie has to be long enough to be visible, but should not go
158 through key sigs. In the 1.5 series the pref.matter - note
159 distance is fixed , so this won't be a problem anymore.
163 if (Note_head::has_interface (l))
168 This correction is due te the shape of the black note head.
170 if (Rhythmic_head::duration_log (l) == 2)
172 left_x = l->extent (l, X_AXIS).linear_combination (where)
176 left_x = l->extent (l, X_AXIS).linear_combination (lambda);
180 if (Note_head::has_interface (l) && Note_head::has_interface (r))
183 + r->extent (commonx,X_AXIS)[LEFT]
184 - l->extent (commonx, X_AXIS)[RIGHT]
189 if (Note_head::has_interface (l))
190 width = r->relative_coordinate (commonx, X_AXIS)
191 - l->extent (commonx, X_AXIS)[RIGHT]
195 - l->extent (commonx, X_AXIS).linear_combination (lambda)
196 + r->extent (commonx, X_AXIS)[LEFT]
202 SCM details = me->get_grob_property ("details");
204 SCM lim // groetjes aan de chirurgendochter.
205 = scm_assq (ly_symbol2scm ("height-limit"),details);
207 Real h_inf = gh_scm2double (ly_cdr (lim)) * staff_space;
208 Real r_0 = gh_scm2double (ly_cdr (scm_assq (ly_symbol2scm ("ratio"),details)));
210 Bezier b = slur_shape (width, h_inf, r_0);
213 I think this better, particularly for small ties. It always allows the user to move ties if
214 they seem in the wrong place
216 TODO: what if 2 heads have different size.
220 Real ypos = Tie::get_position (me) * staff_space/2
221 + dir * gh_scm2double (me->get_grob_property ("y-offset"));;
224 Make sure we don't start on a dots
226 if (Note_head::has_interface (l) && Rhythmic_head::get_dots (l))
228 Grob* dots = Rhythmic_head::get_dots (l);
229 if(fabs (staff_space * Staff_symbol_referencer::get_position (dots) /2
238 todo: prevent ending / staffline collision.
240 todo: tie / stem collision
243 b = slur_shape (width,h_inf, r_0);
245 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 * gh_scm2double (me->get_grob_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)
299 newy = newy + dir * staff_space;
303 Real y0 = b.control_ [0][Y_AXIS];
304 b.control_[2][Y_AXIS] =
305 b.control_[1][Y_AXIS] =
306 (b.control_[1][Y_AXIS] - y0) * ((newy - y0) / (y - y0)) + y0;
309 programming_error ("Tie is nowhere horizontal");
313 SCM controls = SCM_EOL;
315 controls = gh_cons (ly_offset2scm (b.control_[i]), controls);
320 MAKE_SCHEME_CALLBACK (Tie,brew_molecule,1);
322 Tie::brew_molecule (SCM smob)
324 Grob*me = unsmob_grob (smob);
326 SCM cp = me->get_grob_property ("control-points");
329 cp = get_control_points (smob);
330 me->set_grob_property ("control-points", cp);
334 gh_scm2double (me->get_grob_property ("thickness"))
335 * me->get_paper ()->get_realvar (ly_symbol2scm ("linethickness"));
339 for (SCM s= cp; s != SCM_EOL; s = ly_cdr (s))
341 b.control_[i] = ly_scm2offset (ly_car (s));
345 Molecule a = Lookup::slur (b, get_grob_direction (me) * thick, thick);
347 return a.smobbed_copy ();
352 ADD_INTERFACE (Tie,"tie-interface",
353 "A tie connecting two noteheads.\n"
354 "direction = Forced direction for all ties",
355 "y-offset staffline-clearance control-points head-pair details thickness x-gap direction minimum-length");