(parse_symbol_list): Bugfix.
[lilypond/patrick.git] / lily / tie.cc
blob8668d994cad5b992c583d801b5fa4a01c5d94f23
1 /*
2 tie.cc -- implement Tie
4 source file of the GNU LilyPond music typesetter
6 (c) 1997--2005 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7 */
9 #include "tie.hh"
11 #include <math.h>
13 #include "spanner.hh"
14 #include "lookup.hh"
15 #include "output-def.hh"
16 #include "rhythmic-head.hh"
17 #include "bezier.hh"
18 #include "paper-column.hh"
19 #include "warn.hh"
20 #include "staff-symbol-referencer.hh"
21 #include "directional-element-interface.hh"
22 #include "bezier.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?
39 void
40 Tie::set_head (Grob *me, Direction d, Grob *h)
42 assert (!head (me, d));
43 index_set_cell (me->get_property ("head-pair"), 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_property ("head-pair", scm_cons (SCM_EOL, SCM_EOL));
55 Grob *
56 Tie::head (Grob *me, Direction d)
58 SCM c = me->get_property ("head-pair");
60 if (scm_is_pair (c))
61 return unsmob_grob (index_get_cell (c, d));
62 else
63 return 0;
66 int
67 Tie::get_column_rank (Grob *me, Direction d)
69 Spanner *span = dynamic_cast<Spanner *> (me);
70 Grob *h = head (me, d);
71 if (!h)
72 h = span->get_bound (d);
74 Grob *col = dynamic_cast<Item *> (h)->get_column ();
75 return Paper_column::get_rank (col);
78 Real
79 Tie::get_position (Grob *me)
81 Direction d = head (me, LEFT) ? LEFT : RIGHT;
82 return Staff_symbol_referencer::get_position (head (me, d));
86 Default: Put the tie oppositie of the stem [Wanske p231]
88 In case of chords: Tie_column takes over
90 The direction of the Tie is more complicated (See [Ross] p136 and
91 further).
93 (what about linebreaks? )
95 Direction
96 Tie::get_default_dir (Grob *me)
98 Item *sl = head (me, LEFT) ? Rhythmic_head::get_stem (head (me, LEFT)) : 0;
99 Item *sr = head (me, RIGHT) ? Rhythmic_head::get_stem (head (me, RIGHT)) : 0;
100 if (sl && sr)
102 if (get_grob_direction (sl) == UP
103 && get_grob_direction (sr) == UP)
104 return DOWN;
106 else if (sl || sr)
108 Item *s = sl ? sl : sr;
109 return -get_grob_direction (s);
112 return UP;
115 void
116 Tie::set_direction (Grob *me)
118 if (!get_grob_direction (me))
120 if (Tie_column::has_interface (me->get_parent (Y_AXIS)))
121 Tie_column::set_directions (me->get_parent (Y_AXIS));
122 else
123 set_grob_direction (me, Tie::get_default_dir (me));
128 TODO: we should also use thickness for computing the clearance
129 between head and tie. Very thick ties will now touch the note head.
132 Tie::get_control_points (SCM smob)
134 Spanner *me = unsmob_spanner (smob);
135 Direction headdir = CENTER;
136 if (head (me, LEFT))
137 headdir = LEFT;
138 else if (head (me, RIGHT))
139 headdir = RIGHT;
140 else
142 programming_error ("tie without heads");
143 me->suicide ();
144 return SCM_EOL;
147 set_direction (me);
149 Direction dir = get_grob_direction (me);
151 Real staff_space = Staff_symbol_referencer::staff_space (me);
153 Real x_gap_f = robust_scm2double (me->get_property ("x-gap"), 0);
155 Grob *l = me->get_bound (LEFT);
156 Grob *r = me->get_bound (RIGHT);
158 Grob *commonx = me->common_refpoint (l, X_AXIS);
159 commonx = me->common_refpoint (r, X_AXIS);
161 Real left_x;
164 the tie has to be long enough to be visible, but should not go
165 through key sigs. In the 1.5 series the pref.matter - note
166 distance is fixed , so this won't be a problem anymore.
168 Real lambda = 0.9;
170 if (Note_head::has_interface (l))
172 Real where = RIGHT;
175 This correction is due te the shape of the black note head.
177 if (Rhythmic_head::duration_log (l) == 2)
178 where += dir * 0.2;
179 left_x = l->extent (l, X_AXIS).linear_combination (where)
180 + x_gap_f;
182 else
183 left_x = l->extent (l, X_AXIS).linear_combination (lambda);
185 Real width;
186 if (Note_head::has_interface (l) && Note_head::has_interface (r))
188 width
189 = + r->extent (commonx, X_AXIS)[LEFT]
190 - l->extent (commonx, X_AXIS)[RIGHT]
191 -2 * x_gap_f;
193 else
195 if (Note_head::has_interface (l))
196 width = r->relative_coordinate (commonx, X_AXIS)
197 - l->extent (commonx, X_AXIS)[RIGHT]
198 - 2 * x_gap_f;
199 else
200 width
201 = -l->extent (commonx, X_AXIS).linear_combination (lambda)
202 + r->extent (commonx, X_AXIS)[LEFT]
203 - 2 * x_gap_f;
206 SCM details = me->get_property ("details");
208 SCM lim // groetjes aan de chirurgendochter.
209 = scm_assq (ly_symbol2scm ("height-limit"), details);
211 Real h_inf = scm_to_double (scm_cdr (lim)) * staff_space;
212 Real r_0 = scm_to_double (scm_cdr (scm_assq (ly_symbol2scm ("ratio"), details)));
214 Bezier b = slur_shape (width, h_inf, r_0);
217 I think this better, particularly for small ties. It always allows the user to move ties if
218 they seem in the wrong place
220 TODO: what if 2 heads have different size.
224 Real ypos = Tie::get_position (me) * staff_space / 2
225 + dir * scm_to_double (me->get_property ("y-offset"));;
228 Make sure we don't start on a dots
230 if (Note_head::has_interface (l) && Rhythmic_head::get_dots (l))
232 Grob *dots = Rhythmic_head::get_dots (l);
233 if (fabs (staff_space * Staff_symbol_referencer::get_position (dots) / 2
234 - ypos) < 0.5)
235 ypos += 0.5 * dir;
239 todo: prevent ending / staffline collision.
241 todo: tie / stem collision
244 b = slur_shape (width, h_inf, r_0);
245 b.scale (1, dir);
246 b.translate (Offset (left_x, ypos));
249 Avoid colliding of the horizontal part with stafflines.
252 TODO: redo this, heuristic is half-baken, and ties often look ugly
253 as a result.
255 TODO: doesn't work when on staff with even number of lines.
257 Array<Real> horizontal (b.solve_derivative (Offset (1, 0)));
258 if (horizontal.size ())
261 ugh. Doesnt work for non-horizontal curves.
263 Real y = b.curve_point (horizontal[0])[Y_AXIS];
265 Real ry = rint (y / staff_space) * staff_space;
266 Real diff = ry - y;
267 Real newy = y;
269 Real clear = staff_space * scm_to_double (me->get_property ("staffline-clearance"));
271 if (fabs (y)
272 <= Staff_symbol_referencer::staff_radius (me) * staff_space + clear
273 && fabs (diff) < clear)
275 Real y1 = ry + clear;
276 Real y2 = ry - clear;
279 ugh, we shove the 0.5 out of our sleeves.
281 Any way. This test is to make sure that staffline
282 collision avoidance does not result in completely flat
283 ties.
285 if (fabs (y1 - ypos) < 0.5)
286 y1 = y2;
287 else if (fabs (y2 - ypos) < 0.5)
288 y2 = y1;
290 newy = (fabs (y1 - y) < fabs (y2 - y)) ? y1 : y2;
292 // newy = ry - 0.5 * staff_space * sign (diff) ;
295 we don't want horizontal ties
297 if (fabs (newy - b.control_[0][Y_AXIS]) < 1e-2)
298 newy = newy + dir * staff_space;
301 Real y0 = b.control_ [0][Y_AXIS];
302 b.control_[2][Y_AXIS]
303 = b.control_[1][Y_AXIS]
304 = (b.control_[1][Y_AXIS] - y0) * ((newy - y0) / (y - y0)) + y0;
306 else
307 programming_error ("tie is nowhere horizontal");
309 SCM controls = SCM_EOL;
310 for (int i = 4; i--;)
311 controls = scm_cons (ly_offset2scm (b.control_[i]), controls);
312 return controls;
315 MAKE_SCHEME_CALLBACK (Tie, print, 1);
317 Tie::print (SCM smob)
319 Grob *me = unsmob_grob (smob);
321 SCM cp = me->get_property ("control-points");
322 if (!scm_is_pair (cp)) // list is more accurate
324 cp = get_control_points (smob);
325 me->set_property ("control-points", cp);
328 if (!scm_is_pair (cp))
329 return Stencil ().smobbed_copy ();
331 Real staff_thick = Staff_symbol_referencer::line_thickness (me);
332 Real base_thick = robust_scm2double (me->get_property ("thickness"), 1);
333 Real thick = base_thick * staff_thick;
335 Bezier b;
336 int i = 0;
337 for (SCM s = cp; s != SCM_EOL; s = scm_cdr (s))
339 b.control_[i] = ly_scm2offset (scm_car (s));
340 i++;
343 Stencil a;
345 SCM p = me->get_property ("dash-period");
346 SCM f = me->get_property ("dash-fraction");
347 if (scm_is_number (p) && scm_is_number (f))
348 a = Lookup::dashed_slur (b,
349 thick,
350 robust_scm2double (p, 1.0),
351 robust_scm2double (f, 0));
352 else
353 a = Lookup::slur (b,
354 get_grob_direction (me) * staff_thick,
355 thick);
357 return a.smobbed_copy ();
360 ADD_INTERFACE (Tie,
361 "tie-interface",
362 "A tie connecting two noteheads.\n",
364 "y-offset dash-period dash-fraction "
365 "staffline-clearance control-points head-pair "
366 "details thickness x-gap direction minimum-length");