* lily/note-collision.cc (do_shifts): align colliding notes to
[lilypond.git] / lily / tie.cc
blobc13f97f3540f42d454f6036039e7bd2cbe772f28
1 /*
2 tie.cc -- implement Tie
4 source file of the GNU LilyPond music typesetter
6 (c) 1997--2004 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7 */
8 #include <math.h>
11 #include "spanner.hh"
12 #include "lookup.hh"
13 #include "output-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 "stencil.hh"
22 #include "bezier-bow.hh"
23 #include "stem.hh"
24 #include "note-head.hh"
25 #include "tie-column.hh"
28 tie: Connect two noteheads.
30 What if we have
32 c4 ~ \clef bass ; c4 or
34 c4 \staffchange c4
36 do we have non-horizontal ties then?
40 void
41 Tie::set_head (Grob*me,Direction d, Grob * h)
43 assert (!head (me,d));
44 index_set_cell (me->get_property ("head-pair"), d, h->self_scm ());
46 dynamic_cast<Spanner*> (me)->set_bound (d, h);
47 me->add_dependency (h);
50 void
51 Tie::set_interface (Grob*me)
53 me->set_property ("head-pair", scm_cons (SCM_EOL, SCM_EOL));
57 Grob*
58 Tie::head (Grob*me, Direction d)
60 SCM c = me->get_property ("head-pair");
62 if (ly_c_pair_p (c))
63 return unsmob_grob (index_get_cell (c, d));
64 else
65 return 0;
68 Real
69 Tie::get_position (Grob*me)
71 Direction d = head (me,LEFT) ? LEFT:RIGHT;
72 return Staff_symbol_referencer::get_position (head (me,d));
77 Default: Put the tie oppositie of the stem [Wanske p231]
79 In case of chords: Tie_column takes over
81 The direction of the Tie is more complicated (See [Ross] p136 and
82 further).
84 (what about linebreaks? )
87 Direction
88 Tie::get_default_dir (Grob*me)
90 Item * sl = head (me,LEFT) ? Rhythmic_head::get_stem (head (me,LEFT)) :0;
91 Item * sr = head (me,RIGHT) ? Rhythmic_head::get_stem (head (me,RIGHT)) :0;
92 if (sl && sr)
94 if (get_grob_direction (sl) == UP
95 && get_grob_direction (sr) == UP)
96 return DOWN;
98 else if (sl || sr)
100 Item *s = sl ? sl : sr;
101 return - get_grob_direction (s);
105 return UP;
109 void
110 Tie::set_direction (Grob*me)
112 if (!get_grob_direction (me))
114 if (Tie_column::has_interface (me->get_parent (Y_AXIS)))
115 Tie_column::set_directions (me->get_parent (Y_AXIS));
116 else
117 set_grob_direction (me, Tie::get_default_dir (me));
122 TODO: we should also use thickness for computing the clearance
123 between head and tie. Very thick ties will now touch the note head.
127 Tie::get_control_points (SCM smob)
129 Spanner*me = unsmob_spanner (smob);
130 Direction headdir = CENTER;
131 if (head (me,LEFT))
132 headdir = LEFT;
133 else if (head (me,RIGHT))
134 headdir = RIGHT;
135 else
137 programming_error ("Tie without heads.");
138 me->suicide ();
139 return SCM_EOL;
142 set_direction (me);
144 Direction dir = get_grob_direction (me);
146 Real staff_space = Staff_symbol_referencer::staff_space (me);
148 Real x_gap_f = robust_scm2double (me->get_property ("x-gap"), 0);
150 Grob* l = me->get_bound (LEFT);
151 Grob* r = me->get_bound (RIGHT);
153 Grob* commonx = me->common_refpoint (l, X_AXIS);
154 commonx = me->common_refpoint (r, X_AXIS);
156 Real left_x;
159 the tie has to be long enough to be visible, but should not go
160 through key sigs. In the 1.5 series the pref.matter - note
161 distance is fixed , so this won't be a problem anymore.
163 Real lambda = 0.9;
165 if (Note_head::has_interface (l))
167 Real where = RIGHT;
170 This correction is due te the shape of the black note head.
172 if (Rhythmic_head::duration_log (l) == 2)
173 where += dir* 0.2;
174 left_x = l->extent (l, X_AXIS).linear_combination (where)
175 + x_gap_f;
177 else
178 left_x = l->extent (l, X_AXIS).linear_combination (lambda);
181 Real width;
182 if (Note_head::has_interface (l) && Note_head::has_interface (r))
184 width =
185 + r->extent (commonx,X_AXIS)[LEFT]
186 - l->extent (commonx, X_AXIS)[RIGHT]
187 -2 * x_gap_f;
189 else
191 if (Note_head::has_interface (l))
192 width = r->relative_coordinate (commonx, X_AXIS)
193 - l->extent (commonx, X_AXIS)[RIGHT]
194 - 2 * x_gap_f;
195 else
196 width =
197 - l->extent (commonx, X_AXIS).linear_combination (lambda)
198 + r->extent (commonx, X_AXIS)[LEFT]
199 - 2 * x_gap_f;
204 SCM details = me->get_property ("details");
206 SCM lim // groetjes aan de chirurgendochter.
207 = scm_assq (ly_symbol2scm ("height-limit"),details);
209 Real h_inf = ly_scm2double (ly_cdr (lim)) * staff_space;
210 Real r_0 = ly_scm2double (ly_cdr (scm_assq (ly_symbol2scm ("ratio"),details)));
212 Bezier b = slur_shape (width, h_inf, r_0);
215 I think this better, particularly for small ties. It always allows the user to move ties if
216 they seem in the wrong place
218 TODO: what if 2 heads have different size.
222 Real ypos = Tie::get_position (me) * staff_space/2
223 + dir * ly_scm2double (me->get_property ("y-offset"));;
226 Make sure we don't start on a dots
228 if (Note_head::has_interface (l) && Rhythmic_head::get_dots (l))
230 Grob* dots = Rhythmic_head::get_dots (l);
231 if (fabs (staff_space * Staff_symbol_referencer::get_position (dots) /2
232 - ypos) < 0.5)
234 ypos += 0.5 * dir ;
240 todo: prevent ending / staffline collision.
242 todo: tie / stem collision
245 b = slur_shape (width,h_inf, r_0);
246 b.scale (1, dir);
247 b.translate (Offset (left_x, ypos));
251 Avoid colliding of the horizontal part with stafflines.
254 TODO: redo this, heuristic is half-baken, and ties often look ugly
255 as a result.
257 TODO: doesn't work when on staff with even number of lines.
259 Array<Real> horizontal (b.solve_derivative (Offset (1,0)));
260 if (horizontal.size ())
263 ugh. Doesnt work for non-horizontal curves.
265 Real y = b.curve_point (horizontal[0])[Y_AXIS];
267 Real ry = rint (y/staff_space) * staff_space;
268 Real diff = ry - y;
269 Real newy = y;
271 Real clear = staff_space * ly_scm2double (me->get_property ("staffline-clearance"));
273 if (fabs (y) <=
274 Staff_symbol_referencer::staff_radius (me) * staff_space + clear
275 && fabs (diff) < clear)
277 Real y1 = ry + clear;
278 Real y2 = ry - clear;
281 ugh, we shove the 0.5 out of our sleeves.
283 Any way. This test is to make sure that staffline
284 collision avoidance does not result in completely flat
285 ties.
287 if (fabs (y1 - ypos) < 0.5)
288 y1 = y2;
289 else if (fabs (y2 - ypos) < 0.5)
290 y2 = y1;
292 newy = (fabs (y1 - y) < fabs (y2 - y)) ? y1 : y2;
294 // newy = ry - 0.5 * staff_space * sign (diff) ;
297 we don't want horizontal ties
299 if (fabs (newy - b.control_[0][Y_AXIS]) < 1e-2)
301 newy = newy + dir * staff_space;
305 Real y0 = b.control_ [0][Y_AXIS];
306 b.control_[2][Y_AXIS] =
307 b.control_[1][Y_AXIS] =
308 (b.control_[1][Y_AXIS] - y0) * ((newy - y0) / (y - y0)) + y0;
310 else
311 programming_error ("Tie is nowhere horizontal");
315 SCM controls = SCM_EOL;
316 for (int i= 4; i--;)
317 controls = scm_cons (ly_offset2scm (b.control_[i]), controls);
318 return controls;
322 MAKE_SCHEME_CALLBACK (Tie,print,1);
324 Tie::print (SCM smob)
326 Grob*me = unsmob_grob (smob);
328 SCM cp = me->get_property ("control-points");
329 if (!ly_c_pair_p (cp)) // list is more accurate
331 cp = get_control_points (smob);
332 me->set_property ("control-points", cp);
335 if (!ly_c_pair_p (cp))
336 return Stencil ().smobbed_copy ();
338 Real thick
339 = Staff_symbol_referencer::line_thickness (me)
340 * robust_scm2double (me->get_property ("thickness"), 1);
342 Bezier b;
343 int i = 0;
344 for (SCM s= cp; s != SCM_EOL; s = ly_cdr (s))
346 b.control_[i] = ly_scm2offset (ly_car (s));
347 i++;
350 Stencil a = Lookup::slur (b, get_grob_direction (me) * thick, thick);
352 return a.smobbed_copy ();
357 ADD_INTERFACE (Tie,"tie-interface",
358 "A tie connecting two noteheads.\n"
360 "y-offset staffline-clearance control-points head-pair details thickness x-gap direction minimum-length");