2 tie.cc -- implement Tie
4 source file of the GNU LilyPond music typesetter
6 (c) 1997--2002 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, Item * h)
42 assert (!head (me,d));
43 index_set_cell (me->get_grob_property ("heads"), 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 ("heads", gh_cons (SCM_EOL, SCM_EOL));
57 Tie::head (Grob*me, Direction d)
59 SCM c = me->get_grob_property ("heads");
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
82 Tie::get_default_dir (Grob*me)
84 Item * sl = head (me,LEFT) ? Rhythmic_head::get_stem (head (me,LEFT)) :0;
85 Item * sr = head (me,RIGHT) ? Rhythmic_head::get_stem (head (me,RIGHT)) :0;
89 if (Directional_element_interface::get (sl) == UP
90 && Directional_element_interface::get (sr) == UP)
95 Item *s = sl ? sl : sr;
96 return - Directional_element_interface::get (s);
105 Tie::get_control_points (SCM smob)
107 Spanner*me = unsmob_spanner (smob);
108 Direction headdir = CENTER;
111 else if (head (me,RIGHT))
115 programming_error ("Tie without heads.");
117 return SCM_UNSPECIFIED;
121 if (!Directional_element_interface::get (me))
122 Directional_element_interface::set (me, Tie::get_default_dir (me));
123 Direction dir = Directional_element_interface::get (me);
125 Real staff_space = Staff_symbol_referencer::staff_space (me);
127 Real x_gap_f = gh_scm2double (me->get_grob_property ("x-gap"));
129 Grob* l = me->get_bound (LEFT);
130 Grob* r = me->get_bound (RIGHT);
132 Grob* commonx = me->common_refpoint (l, X_AXIS);
133 commonx = me->common_refpoint (r, X_AXIS);
138 the tie has to be long enough to be visible, but should not go
139 through key sigs. In the 1.5 series the pref.matter - note
140 distance is fixed , so this won't be a problem anymore.
144 if (Note_head::has_interface (l))
149 This correction is due te the shape of the black note head.
151 if (Rhythmic_head::duration_log (l) == 2)
153 left_x = l->extent (l, X_AXIS).linear_combination (where)
157 left_x = l->extent (l, X_AXIS).linear_combination (lambda);
161 if (Note_head::has_interface (l) && Note_head::has_interface (r))
164 + r->extent (commonx,X_AXIS)[LEFT]
165 - l->extent (commonx, X_AXIS)[RIGHT]
170 if (Note_head::has_interface (l))
171 width = r->relative_coordinate (commonx, X_AXIS)
172 - l->extent (commonx, X_AXIS)[RIGHT]
176 - l->extent (commonx, X_AXIS).linear_combination (lambda)
177 + r->extent (commonx, X_AXIS)[LEFT]
183 SCM details = me->get_grob_property ("details");
185 SCM lim // groetjes aan de chirurgendochter.
186 = scm_assq (ly_symbol2scm ("height-limit"),details);
188 Real h_inf = gh_scm2double (ly_cdr (lim)) * staff_space;
189 Real r_0 = gh_scm2double (ly_cdr (scm_assq (ly_symbol2scm ("ratio"),details)));
191 Bezier b = slur_shape (width, h_inf, r_0);
194 I think this better, particularly for small ties. It always allows the user to move ties if
195 they seem in the wrong place
197 TODO: what if 2 heads have different size.
201 Real ypos = Tie::get_position (me) * staff_space/2
202 + dir * gh_scm2double (me->get_grob_property ("y-offset"));;
205 Make sure we don't start on a dots
207 if (Note_head::has_interface (l) && Rhythmic_head::get_dots (l))
209 Grob* dots = Rhythmic_head::get_dots (l);
210 if(fabs (staff_space * Staff_symbol_referencer::get_position (dots) /2
219 todo: prevent ending / staffline collision.
221 todo: tie / stem collision
224 b = slur_shape (width,h_inf, r_0);
226 b.translate (Offset (left_x, ypos));
230 Avoid colliding of the horizontal part with stafflines.
233 TODO: redo this, heuristic is half-baken, and ties often look ugly
236 TODO: doesn't work when on staff with even number of lines.
238 Array<Real> horizontal (b.solve_derivative (Offset (1,0)));
239 if (horizontal.size ())
242 ugh. Doesnt work for non-horizontal curves.
244 Real y = b.curve_point (horizontal[0])[Y_AXIS];
246 Real ry = rint (y/staff_space) * staff_space;
250 Real clear = staff_space * gh_scm2double (me->get_grob_property ("staffline-clearance"));
252 if (fabs (y) <= Staff_symbol_referencer::staff_radius (me)
253 && fabs (diff) < clear)
255 Real y1 = ry + clear;
256 Real y2 = ry - clear;
259 ugh, we shove the 0.5 out of our sleeves.
261 Any way. This test is to make sure that staffline
262 collision avoidance does not result in completely flat
265 if (fabs (y1 - ypos) < 0.5)
267 else if (fabs (y2 - ypos) < 0.5)
270 newy = (fabs (y1 - y) < fabs (y2 - y)) ? y1 : y2;
272 // newy = ry - 0.5 * staff_space * sign (diff) ;
275 we don't want horizontal ties
277 if (fabs (newy - b.control_[0][Y_AXIS]) < 1e-2)
279 newy = newy + dir * staff_space;
283 Real y0 = b.control_ [0][Y_AXIS];
284 b.control_[2][Y_AXIS] =
285 b.control_[1][Y_AXIS] =
286 (b.control_[1][Y_AXIS] - y0) * ((newy - y0) / (y - y0)) + y0;
289 programming_error ("Tie is nowhere horizontal");
293 SCM controls = SCM_EOL;
295 controls = gh_cons (ly_offset2scm (b.control_[i]), controls);
300 MAKE_SCHEME_CALLBACK (Tie,brew_molecule,1);
302 Tie::brew_molecule (SCM smob)
304 Grob*me = unsmob_grob (smob);
306 SCM cp = me->get_grob_property ("control-points");
309 cp = get_control_points (smob);
310 me->set_grob_property ("control-points", cp);
314 gh_scm2double (me->get_grob_property ("thickness"))
315 * me->get_paper ()->get_var ("linethickness");
319 for (SCM s= cp; s != SCM_EOL; s = ly_cdr (s))
321 b.control_[i] = ly_scm2offset (ly_car (s));
325 Molecule a = Lookup::slur (b, Directional_element_interface::get (me) * thick, thick);
327 return a.smobbed_copy ();
332 ADD_INTERFACE (Tie,"tie-interface",
333 "A tie connecting two noteheads.
334 direction = Forced direction for all ties",
335 "y-offset staffline-clearance control-points heads details thickness x-gap direction minimum-length");