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"
27 tie: Connect two noteheads.
31 c4 ~ \clef bass ; c4 or
35 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_grob_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_grob_property ("head-pair", gh_cons (SCM_EOL, SCM_EOL));
57 Tie::head (Grob*me, Direction d)
59 SCM c = me->get_grob_property ("head-pair");
60 c = index_get_cell (c, d);
62 return unsmob_grob (c);
66 Tie::get_position (Grob*me)
68 Direction d = head (me,LEFT) ? LEFT:RIGHT;
69 return Staff_symbol_referencer::get_position (head (me,d));
74 Default: Put the tie oppositie of the stem [Wanske p231]
76 In case of chords: Tie_column takes over
78 The direction of the Tie is more complicated (See [Ross] p136 and
81 (what about linebreaks? )
85 Tie::get_default_dir (Grob*me)
87 Item * sl = head (me,LEFT) ? Rhythmic_head::get_stem (head (me,LEFT)) :0;
88 Item * sr = head (me,RIGHT) ? Rhythmic_head::get_stem (head (me,RIGHT)) :0;
91 if (get_grob_direction (sl) == UP
92 && get_grob_direction (sr) == UP)
97 Item *s = sl ? sl : sr;
98 return - get_grob_direction (s);
106 TODO: we should also use thickness for computing the clearance
107 between head and tie. Very thick ties will now touch the note head.
111 Tie::get_control_points (SCM smob)
113 Spanner*me = unsmob_spanner (smob);
114 Direction headdir = CENTER;
117 else if (head (me,RIGHT))
121 programming_error ("Tie without heads.");
123 return SCM_UNSPECIFIED;
127 if (!get_grob_direction (me))
128 set_grob_direction (me, Tie::get_default_dir (me));
129 Direction dir = get_grob_direction (me);
131 Real staff_space = Staff_symbol_referencer::staff_space (me);
133 Real x_gap_f = robust_scm2double (me->get_grob_property ("x-gap"), 0);
135 Grob* l = me->get_bound (LEFT);
136 Grob* r = me->get_bound (RIGHT);
138 Grob* commonx = me->common_refpoint (l, X_AXIS);
139 commonx = me->common_refpoint (r, X_AXIS);
144 the tie has to be long enough to be visible, but should not go
145 through key sigs. In the 1.5 series the pref.matter - note
146 distance is fixed , so this won't be a problem anymore.
150 if (Note_head::has_interface (l))
155 This correction is due te the shape of the black note head.
157 if (Rhythmic_head::duration_log (l) == 2)
159 left_x = l->extent (l, X_AXIS).linear_combination (where)
163 left_x = l->extent (l, X_AXIS).linear_combination (lambda);
167 if (Note_head::has_interface (l) && Note_head::has_interface (r))
170 + r->extent (commonx,X_AXIS)[LEFT]
171 - l->extent (commonx, X_AXIS)[RIGHT]
176 if (Note_head::has_interface (l))
177 width = r->relative_coordinate (commonx, X_AXIS)
178 - l->extent (commonx, X_AXIS)[RIGHT]
182 - l->extent (commonx, X_AXIS).linear_combination (lambda)
183 + r->extent (commonx, X_AXIS)[LEFT]
189 SCM details = me->get_grob_property ("details");
191 SCM lim // groetjes aan de chirurgendochter.
192 = scm_assq (ly_symbol2scm ("height-limit"),details);
194 Real h_inf = gh_scm2double (ly_cdr (lim)) * staff_space;
195 Real r_0 = gh_scm2double (ly_cdr (scm_assq (ly_symbol2scm ("ratio"),details)));
197 Bezier b = slur_shape (width, h_inf, r_0);
200 I think this better, particularly for small ties. It always allows the user to move ties if
201 they seem in the wrong place
203 TODO: what if 2 heads have different size.
207 Real ypos = Tie::get_position (me) * staff_space/2
208 + dir * gh_scm2double (me->get_grob_property ("y-offset"));;
211 Make sure we don't start on a dots
213 if (Note_head::has_interface (l) && Rhythmic_head::get_dots (l))
215 Grob* dots = Rhythmic_head::get_dots (l);
216 if(fabs (staff_space * Staff_symbol_referencer::get_position (dots) /2
225 todo: prevent ending / staffline collision.
227 todo: tie / stem collision
230 b = slur_shape (width,h_inf, r_0);
232 b.translate (Offset (left_x, ypos));
236 Avoid colliding of the horizontal part with stafflines.
239 TODO: redo this, heuristic is half-baken, and ties often look ugly
242 TODO: doesn't work when on staff with even number of lines.
244 Array<Real> horizontal (b.solve_derivative (Offset (1,0)));
245 if (horizontal.size ())
248 ugh. Doesnt work for non-horizontal curves.
250 Real y = b.curve_point (horizontal[0])[Y_AXIS];
252 Real ry = rint (y/staff_space) * staff_space;
256 Real clear = staff_space * gh_scm2double (me->get_grob_property ("staffline-clearance"));
259 Staff_symbol_referencer::staff_radius (me) * staff_space + clear
260 && fabs (diff) < clear)
262 Real y1 = ry + clear;
263 Real y2 = ry - clear;
266 ugh, we shove the 0.5 out of our sleeves.
268 Any way. This test is to make sure that staffline
269 collision avoidance does not result in completely flat
272 if (fabs (y1 - ypos) < 0.5)
274 else if (fabs (y2 - ypos) < 0.5)
277 newy = (fabs (y1 - y) < fabs (y2 - y)) ? y1 : y2;
279 // newy = ry - 0.5 * staff_space * sign (diff) ;
282 we don't want horizontal ties
284 if (fabs (newy - b.control_[0][Y_AXIS]) < 1e-2)
286 newy = newy + dir * staff_space;
290 Real y0 = b.control_ [0][Y_AXIS];
291 b.control_[2][Y_AXIS] =
292 b.control_[1][Y_AXIS] =
293 (b.control_[1][Y_AXIS] - y0) * ((newy - y0) / (y - y0)) + y0;
296 programming_error ("Tie is nowhere horizontal");
300 SCM controls = SCM_EOL;
302 controls = gh_cons (ly_offset2scm (b.control_[i]), controls);
307 MAKE_SCHEME_CALLBACK (Tie,brew_molecule,1);
309 Tie::brew_molecule (SCM smob)
311 Grob*me = unsmob_grob (smob);
313 SCM cp = me->get_grob_property ("control-points");
316 cp = get_control_points (smob);
317 me->set_grob_property ("control-points", cp);
321 gh_scm2double (me->get_grob_property ("thickness"))
322 * me->get_paper ()->get_realvar (ly_symbol2scm ("linethickness"));
326 for (SCM s= cp; s != SCM_EOL; s = ly_cdr (s))
328 b.control_[i] = ly_scm2offset (ly_car (s));
332 Molecule a = Lookup::slur (b, get_grob_direction (me) * thick, thick);
334 return a.smobbed_copy ();
339 ADD_INTERFACE (Tie,"tie-interface",
340 "A tie connecting two noteheads.\n"
341 "direction = Forced direction for all ties",
342 "y-offset staffline-clearance control-points head-pair details thickness x-gap direction minimum-length");