release commit
[lilypond.git] / lily / tie.cc
blob99cf426d5c77a5ce8d09aa7d2e89ae8f1b72543a
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;
105 Tie::get_control_points (SCM smob)
107 Spanner*me = unsmob_spanner (smob);
108 Direction headdir = CENTER;
109 if (head (me,LEFT))
110 headdir = LEFT;
111 else if (head (me,RIGHT))
112 headdir = RIGHT;
113 else
115 programming_error ("Tie without heads.");
116 me->suicide ();
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);
135 Real left_x;
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.
142 Real lambda = 0.9;
144 if (Note_head::has_interface (l))
146 Real where = RIGHT;
149 This correction is due te the shape of the black note head.
151 if (Rhythmic_head::duration_log (l) == 2)
152 where += dir* 0.2;
153 left_x = l->extent (l, X_AXIS).linear_combination (where)
154 + x_gap_f;
156 else
157 left_x = l->extent (l, X_AXIS).linear_combination (lambda);
160 Real width;
161 if (Note_head::has_interface (l) && Note_head::has_interface (r))
163 width =
164 + r->extent (commonx,X_AXIS)[LEFT]
165 - l->extent (commonx, X_AXIS)[RIGHT]
166 -2 * x_gap_f;
168 else
170 if (Note_head::has_interface (l))
171 width = r->relative_coordinate (commonx, X_AXIS)
172 - l->extent (commonx, X_AXIS)[RIGHT]
173 - 2 * x_gap_f;
174 else
175 width =
176 - l->extent (commonx, X_AXIS).linear_combination (lambda)
177 + r->extent (commonx, X_AXIS)[LEFT]
178 - 2 * x_gap_f;
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
211 - ypos) < 0.5)
213 ypos += 0.5 * dir ;
219 todo: prevent ending / staffline collision.
221 todo: tie / stem collision
224 b = slur_shape (width,h_inf, r_0);
225 b.scale (1, dir);
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
234 as a result.
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;
247 Real diff = ry - y;
248 Real newy = y;
250 Real clear = staff_space * gh_scm2double (me->get_grob_property ("staffline-clearance"));
252 if (fabs (y) <=
253 Staff_symbol_referencer::staff_radius (me) * staff_space + clear
254 && fabs (diff) < clear)
256 Real y1 = ry + clear;
257 Real y2 = ry - clear;
260 ugh, we shove the 0.5 out of our sleeves.
262 Any way. This test is to make sure that staffline
263 collision avoidance does not result in completely flat
264 ties.
266 if (fabs (y1 - ypos) < 0.5)
267 y1 = y2;
268 else if (fabs (y2 - ypos) < 0.5)
269 y2 = y1;
271 newy = (fabs (y1 - y) < fabs (y2 - y)) ? y1 : y2;
273 // newy = ry - 0.5 * staff_space * sign (diff) ;
276 we don't want horizontal ties
278 if (fabs (newy - b.control_[0][Y_AXIS]) < 1e-2)
280 newy = newy + dir * staff_space;
284 Real y0 = b.control_ [0][Y_AXIS];
285 b.control_[2][Y_AXIS] =
286 b.control_[1][Y_AXIS] =
287 (b.control_[1][Y_AXIS] - y0) * ((newy - y0) / (y - y0)) + y0;
289 else
290 programming_error ("Tie is nowhere horizontal");
294 SCM controls = SCM_EOL;
295 for (int i= 4; i--;)
296 controls = gh_cons (ly_offset2scm (b.control_[i]), controls);
297 return controls;
301 MAKE_SCHEME_CALLBACK (Tie,brew_molecule,1);
303 Tie::brew_molecule (SCM smob)
305 Grob*me = unsmob_grob (smob);
307 SCM cp = me->get_grob_property ("control-points");
308 if (cp == SCM_EOL)
310 cp = get_control_points (smob);
311 me->set_grob_property ("control-points", cp);
314 Real thick =
315 gh_scm2double (me->get_grob_property ("thickness"))
316 * me->get_paper ()->get_var ("linethickness");
318 Bezier b;
319 int i = 0;
320 for (SCM s= cp; s != SCM_EOL; s = ly_cdr (s))
322 b.control_[i] = ly_scm2offset (ly_car (s));
323 i++;
326 Molecule a = Lookup::slur (b, Directional_element_interface::get (me) * thick, thick);
328 return a.smobbed_copy ();
333 ADD_INTERFACE (Tie,"tie-interface",
334 "A tie connecting two noteheads.\n"
335 "direction = Forced direction for all ties",
336 "y-offset staffline-clearance control-points heads details thickness x-gap direction minimum-length");