(staff_eligible): new function.
[lilypond.git] / lily / tie.cc
blobf314d8bc1727f544a6e899024ac5404c61fef6ad
1 /*
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>
7 */
8 #include <math.h>
11 #include "spanner.hh"
12 #include "lookup.hh"
13 #include "paper-def.hh"
14 #include "tie.hh"
15 #include "rhythmic-head.hh"
16 #include "bezier.hh"
17 #include "paper-column.hh"
18 #include "warn.hh"
19 #include "staff-symbol-referencer.hh"
20 #include "directional-element-interface.hh"
21 #include "molecule.hh"
22 #include "bezier-bow.hh"
23 #include "stem.hh"
24 #include "note-head.hh"
27 tie: Connect two noteheads.
29 What if we have
31 c4 ~ \clef bass ; c4 or
33 c4 \staffchange c4
35 do we have non-horizontal ties then?
39 void
40 Tie::set_head (Grob*me,Direction d, Grob * 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);
49 void
50 Tie::set_interface (Grob*me)
52 me->set_grob_property ("heads", gh_cons (SCM_EOL, SCM_EOL));
56 Grob*
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);
65 Real
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
79 further).
81 Direction
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;
87 if (sl && sr)
89 if (Directional_element_interface::get (sl) == UP
90 && Directional_element_interface::get (sr) == UP)
91 return DOWN;
93 else if (sl || sr)
95 Item *s = sl ? sl : sr;
96 return - Directional_element_interface::get (s);
100 return UP;
104 TODO: we should also use thickness for computing the clearance
105 between head and tie. Very thick ties will now touch the note head.
109 Tie::get_control_points (SCM smob)
111 Spanner*me = unsmob_spanner (smob);
112 Direction headdir = CENTER;
113 if (head (me,LEFT))
114 headdir = LEFT;
115 else if (head (me,RIGHT))
116 headdir = RIGHT;
117 else
119 programming_error ("Tie without heads.");
120 me->suicide ();
121 return SCM_UNSPECIFIED;
125 if (!Directional_element_interface::get (me))
126 Directional_element_interface::set (me, Tie::get_default_dir (me));
127 Direction dir = Directional_element_interface::get (me);
129 Real staff_space = Staff_symbol_referencer::staff_space (me);
131 Real x_gap_f = gh_scm2double (me->get_grob_property ("x-gap"));
133 Grob* l = me->get_bound (LEFT);
134 Grob* r = me->get_bound (RIGHT);
136 Grob* commonx = me->common_refpoint (l, X_AXIS);
137 commonx = me->common_refpoint (r, X_AXIS);
139 Real left_x;
142 the tie has to be long enough to be visible, but should not go
143 through key sigs. In the 1.5 series the pref.matter - note
144 distance is fixed , so this won't be a problem anymore.
146 Real lambda = 0.9;
148 if (Note_head::has_interface (l))
150 Real where = RIGHT;
153 This correction is due te the shape of the black note head.
155 if (Rhythmic_head::duration_log (l) == 2)
156 where += dir* 0.2;
157 left_x = l->extent (l, X_AXIS).linear_combination (where)
158 + x_gap_f;
160 else
161 left_x = l->extent (l, X_AXIS).linear_combination (lambda);
164 Real width;
165 if (Note_head::has_interface (l) && Note_head::has_interface (r))
167 width =
168 + r->extent (commonx,X_AXIS)[LEFT]
169 - l->extent (commonx, X_AXIS)[RIGHT]
170 -2 * x_gap_f;
172 else
174 if (Note_head::has_interface (l))
175 width = r->relative_coordinate (commonx, X_AXIS)
176 - l->extent (commonx, X_AXIS)[RIGHT]
177 - 2 * x_gap_f;
178 else
179 width =
180 - l->extent (commonx, X_AXIS).linear_combination (lambda)
181 + r->extent (commonx, X_AXIS)[LEFT]
182 - 2 * x_gap_f;
187 SCM details = me->get_grob_property ("details");
189 SCM lim // groetjes aan de chirurgendochter.
190 = scm_assq (ly_symbol2scm ("height-limit"),details);
192 Real h_inf = gh_scm2double (ly_cdr (lim)) * staff_space;
193 Real r_0 = gh_scm2double (ly_cdr (scm_assq (ly_symbol2scm ("ratio"),details)));
195 Bezier b = slur_shape (width, h_inf, r_0);
198 I think this better, particularly for small ties. It always allows the user to move ties if
199 they seem in the wrong place
201 TODO: what if 2 heads have different size.
205 Real ypos = Tie::get_position (me) * staff_space/2
206 + dir * gh_scm2double (me->get_grob_property ("y-offset"));;
209 Make sure we don't start on a dots
211 if (Note_head::has_interface (l) && Rhythmic_head::get_dots (l))
213 Grob* dots = Rhythmic_head::get_dots (l);
214 if(fabs (staff_space * Staff_symbol_referencer::get_position (dots) /2
215 - ypos) < 0.5)
217 ypos += 0.5 * dir ;
223 todo: prevent ending / staffline collision.
225 todo: tie / stem collision
228 b = slur_shape (width,h_inf, r_0);
229 b.scale (1, dir);
230 b.translate (Offset (left_x, ypos));
234 Avoid colliding of the horizontal part with stafflines.
237 TODO: redo this, heuristic is half-baken, and ties often look ugly
238 as a result.
240 TODO: doesn't work when on staff with even number of lines.
242 Array<Real> horizontal (b.solve_derivative (Offset (1,0)));
243 if (horizontal.size ())
246 ugh. Doesnt work for non-horizontal curves.
248 Real y = b.curve_point (horizontal[0])[Y_AXIS];
250 Real ry = rint (y/staff_space) * staff_space;
251 Real diff = ry - y;
252 Real newy = y;
254 Real clear = staff_space * gh_scm2double (me->get_grob_property ("staffline-clearance"));
256 if (fabs (y) <=
257 Staff_symbol_referencer::staff_radius (me) * staff_space + clear
258 && fabs (diff) < clear)
260 Real y1 = ry + clear;
261 Real y2 = ry - clear;
264 ugh, we shove the 0.5 out of our sleeves.
266 Any way. This test is to make sure that staffline
267 collision avoidance does not result in completely flat
268 ties.
270 if (fabs (y1 - ypos) < 0.5)
271 y1 = y2;
272 else if (fabs (y2 - ypos) < 0.5)
273 y2 = y1;
275 newy = (fabs (y1 - y) < fabs (y2 - y)) ? y1 : y2;
277 // newy = ry - 0.5 * staff_space * sign (diff) ;
280 we don't want horizontal ties
282 if (fabs (newy - b.control_[0][Y_AXIS]) < 1e-2)
284 newy = newy + dir * staff_space;
288 Real y0 = b.control_ [0][Y_AXIS];
289 b.control_[2][Y_AXIS] =
290 b.control_[1][Y_AXIS] =
291 (b.control_[1][Y_AXIS] - y0) * ((newy - y0) / (y - y0)) + y0;
293 else
294 programming_error ("Tie is nowhere horizontal");
298 SCM controls = SCM_EOL;
299 for (int i= 4; i--;)
300 controls = gh_cons (ly_offset2scm (b.control_[i]), controls);
301 return controls;
305 MAKE_SCHEME_CALLBACK (Tie,brew_molecule,1);
307 Tie::brew_molecule (SCM smob)
309 Grob*me = unsmob_grob (smob);
311 SCM cp = me->get_grob_property ("control-points");
312 if (cp == SCM_EOL)
314 cp = get_control_points (smob);
315 me->set_grob_property ("control-points", cp);
318 Real thick =
319 gh_scm2double (me->get_grob_property ("thickness"))
320 * me->get_paper ()->get_realvar (ly_symbol2scm ("linethickness"));
322 Bezier b;
323 int i = 0;
324 for (SCM s= cp; s != SCM_EOL; s = ly_cdr (s))
326 b.control_[i] = ly_scm2offset (ly_car (s));
327 i++;
330 Molecule a = Lookup::slur (b, Directional_element_interface::get (me) * thick, thick);
332 return a.smobbed_copy ();
337 ADD_INTERFACE (Tie,"tie-interface",
338 "A tie connecting two noteheads.\n"
339 "direction = Forced direction for all ties",
340 "y-offset staffline-clearance control-points heads details thickness x-gap direction minimum-length");